-
Notifications
You must be signed in to change notification settings - Fork 75
Proposal: SecurityInfo in WebRequest
#899
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,193 @@ | ||||||
| # Proposal: `SecurityInfo` in `webRequest` | ||||||
|
|
||||||
| **Summary** | ||||||
|
|
||||||
| An API proposal to extend the `webRequest` API to provide TLS/QUIC certificate information and connection state for a given request. | ||||||
|
|
||||||
| **Document Metadata** | ||||||
|
|
||||||
| **Author:** [Vlad Krot](https://github.com/vkrot-cell) | ||||||
|
|
||||||
| **Sponsoring Browser:** Google Chrome | ||||||
|
|
||||||
| **Contributors:** <other contributors emails or GitHub handles> | ||||||
|
|
||||||
| **Created:** 2025-11-05 | ||||||
|
|
||||||
| **Related Issues:** | ||||||
| * [Mozilla Bug 1322748](https://bugzilla.mozilla.org/show_bug.cgi?id=1322748) | ||||||
| * [Previous abandoned Chromium Proposal](https://github.com/EFForg/webrequest-tlsinfo-api/blob/master/proposal.md) | ||||||
|
|
||||||
| ## Motivation | ||||||
|
|
||||||
| ### Objective | ||||||
|
|
||||||
| This API enables extensions to inspect the server certificate of a web request after a secure connection (TLS/QUIC) has been established. This fulfills a long-standing developer need for TLS introspection, allowing for the creation of advanced security and debugging tools. | ||||||
|
|
||||||
| #### Use Cases | ||||||
|
|
||||||
| The lack of TLS introspection prevents the development of a number of security-focused browser extensions. This API would enable extensions to: | ||||||
| * Replicate the functionality of security scoring tools. | ||||||
| * Provide more granular or prominent certificate chain introspection than the browser's built-in UI. | ||||||
| * Attempt to detect Man-in-the-Middle (MITM) attacks. | ||||||
| * Assist in debugging TLS deployment and configuration issues. | ||||||
| * Enforce custom security policies by blocking requests with insecure or non-compliant certificates. | ||||||
|
|
||||||
| ### Known Consumers | ||||||
|
|
||||||
| This API would facilitate the migration of existing Firefox extensions to Chrome and other browsers that adopt this specification. | ||||||
| A notable example is [IndicateTLS](https://addons.mozilla.org/en-US/firefox/addon/indicatetls/), which relies on similar functionality. | ||||||
|
|
||||||
| ## Specification | ||||||
|
|
||||||
| ### Schema | ||||||
|
|
||||||
| This proposal introduces a new optional field, `securityInfo`, to the details object of the `webRequest.onHeadersReceived` event. Its presence is controlled by new `OnHeadersReceivedOptions`. | ||||||
|
|
||||||
| ```typescript | ||||||
|
|
||||||
| export enum OnHeadersReceivedOptions { | ||||||
| // .. old fields, | ||||||
|
|
||||||
| // If used, then securityInfo will be provided. | ||||||
| "securityInfo", | ||||||
| // The option is used to provide securityInfo with raw bytes of a server certificate. | ||||||
| "securityInfoRawDer" | ||||||
| } | ||||||
|
|
||||||
| export interface Fingerprint { | ||||||
vkrot-cell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| /** | ||||||
| * SHA-256 hash of the certificate. | ||||||
| */ | ||||||
| sha256: string; | ||||||
| } | ||||||
|
|
||||||
| export interface CertificateInfo { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Firefox includes more properties in Question: Did you consciously choose to omit the other documented fields? Was there any particular reason to do so? extraInfoSpec is specified to accept
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Did you consciously choose to omit the other documented fields? Was there any particular reason to do so?" - So I only included fields necessary to accomplish "google internal" goal. I did not evaluate deeply how extension devs would use it. Thank you for this research.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with you on this: extraInfoSpec is specified to accept securityInfo and securityInfoRawDer, but without other properties the securityInfo option is of limited use. Most extensions don't pass the rawDER option However, rawDER is absolute must to include for "google-internal" needs. And yes, securityInfo then is not super useful, only fingerprint and state. Is it a blocker to not have enough fields? |
||||||
| fingerprint: Fingerprint; | ||||||
| rawDER: Uint8Array; | ||||||
vkrot-cell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Represents the state of the network connection. | ||||||
| */ | ||||||
| export enum ConnectionState { | ||||||
| /** | ||||||
| * The TLS handshake failed (for example, the certificate has expired). | ||||||
| */ | ||||||
| Broken = "broken", | ||||||
|
|
||||||
| /** | ||||||
| * The connection is not a TLS connection. | ||||||
| */ | ||||||
| Insecure = "insecure", | ||||||
|
|
||||||
| /** | ||||||
| * The connection is a secure TLS connection. | ||||||
| */ | ||||||
| Secure = "secure" | ||||||
| } | ||||||
|
|
||||||
| export interface SecurityInfo { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This proposal only includes
There are more properties, see #882 (comment) for an example of the actual API result, The properties are currently listed in "future work". Even if you're going to implement this in two steps, it would still be nice for the proposal to be complete. Are you considering to implement additional fields, and if so, which? |
||||||
| /** | ||||||
| * The array contains a single CertificateInfo object, for the server certificate, or empty in case | ||||||
| * the connection state is "insecure" (http). | ||||||
| */ | ||||||
| certificates: CertificateInfo[]; | ||||||
vkrot-cell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| /** | ||||||
| * State of the connection. | ||||||
| */ | ||||||
| state: ConnectionState; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As commented in #882 (comment), just I see that you have specified the "broken" behavior for the scenario of "user has explicitely allowed it in the browser UI", below. This is not what Firefox has currently implemented. "broken" is a really broken state; if the certificate is good other than the expiry date, and the user accepted it, then the state is considered "secure" in Firefox.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I answered in #882 (comment) |
||||||
| } | ||||||
|
|
||||||
| export interface OnHeadersReceivedDetails { | ||||||
| // ... existing fields | ||||||
| /** | ||||||
| * Information about the security of the connection. This is only | ||||||
| * present if "securityInfo" is passed in the extraInfoSpec parameter | ||||||
| * and the request used a secure connection. | ||||||
| */ | ||||||
| securityInfo?: SecurityInfo; | ||||||
| } | ||||||
|
|
||||||
| ``` | ||||||
|
|
||||||
| ### Behavior | ||||||
|
|
||||||
| * New `onHeadersReceivedOptions` are necessary for performance reasons. They specify whether ssl data must be kept for as long as the web request is alive. Without them, unnecessary data can be kept for longer. Which is very undesirable especially in post quantum cryptography, since certificates can take a significant amount of memory space. | ||||||
| Note that Firefox has no such an option, because it only supports blocking web requests call while there’s still ssl data. | ||||||
|
Comment on lines
+120
to
+121
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part can be dropped now. We agreed to include it in the event details, and Firefox could also implement that.
Suggested change
|
||||||
|
|
||||||
| * A new `securityInfo` object can be obtained in the `onHeadersReceived` event listener. | ||||||
|
|
||||||
| * To receive this information, an extension **must** include `"securityInfo"` or `"securityInfoRawDer"` in the `extraInfoSpec` array when calling `addListener`. This opt-in design prevents performance overhead for the majority of extensions that don't need this data. | ||||||
|
|
||||||
| * The `securityInfo` object will only be populated for requests made over a secure protocol (e.g., HTTPS, WSS) where the TLS/QUIC handshake has successfully completed or also in case of certificate errors. | ||||||
| Browsers interrupt connections when there's a certificate error, unless user has explicitely allowed it in the browser UI, only in this case it is possible to have SecurityInfo with `state = "broken"`. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have commented elsewhere; in Firefox |
||||||
|
|
||||||
| * `certificates` - will contain only the leaf server certificate. This is done for future extensibility and Firefox API compatibility, because there they also provide a leaf if [certificateChain](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/getSecurityInfo#certificatechain) is not included in getSecurityInfo options. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: the majority of extensions calling
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I totally agree, but still I would prefer to leave this for another iteration of the API, which was developed to be compatible with this option. |
||||||
|
|
||||||
| * `state` - State of the connection. One of: | ||||||
| * `"broken"`: the TLS handshake failed (for example, the certificate has expired) | ||||||
| * `"insecure"`: the connection is not a TLS connection | ||||||
| * `"secure"`: the connection is a secure TLS connection | ||||||
| * Note that Firefox extension API has a `“weak”` state of connection, which does not exist in Chrome. | ||||||
vkrot-cell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| * The `CertificateInfo.rawDER` field contains the raw certificate bytes in DER format, which can be parsed by the extension using a third-party library. This field is only provided if `extraInfoSpec` array includes `"securityInfoRawDer"`. The reason for it is a performance optimization to not pass raw bytes when | ||||||
| it is not necessary, and compatibility with Firefox extensions API. | ||||||
|
|
||||||
| ### Example | ||||||
|
|
||||||
| Here is an example of an extension listening for web requests to log the server's certificate fingerprint. | ||||||
|
|
||||||
| ```javascript | ||||||
| chrome.webRequest.onHeadersReceived.addListener( | ||||||
| (details) => { | ||||||
| if (details.securityInfo && details.securityInfo.state == "secure") { | ||||||
| console.log(`Received certificate for ${details.url}. The fingerprint is ${details.securityInfo.certificates[0].fingerprint.sha256}`); | ||||||
| } | ||||||
| }, | ||||||
| // Filter for requests. | ||||||
| { urls: ["<all_urls>"] }, | ||||||
| // Opt-in to receive the security info. | ||||||
| ["securityInfo"] | ||||||
| ); | ||||||
| ``` | ||||||
|
|
||||||
| ## Security and Privacy | ||||||
|
|
||||||
| ### Exposed Sensitive Data | ||||||
|
|
||||||
| This API exposes the server's leaf certificate for a given connection. This is public information that is exchanged as part of any standard TLS handshake. | ||||||
| So, no sensitive data is exposed. | ||||||
|
|
||||||
| ## Alternatives | ||||||
|
|
||||||
| ### Existing Workarounds | ||||||
|
|
||||||
| Currently, extensions can only approximate this functionality by capturing a request's URL and sending it to a separate backend service. This service must then perform its own TLS handshake with the destination server to retrieve the certificate. This approach has significant drawbacks: | ||||||
|
|
||||||
| * **Inaccuracy**: It does not guarantee that the extension sees the same certificate the browser saw. This can be caused by load balancing, geographic routing, or a Man-in-the-Middle (MITM) attack. | ||||||
| * **Performance**: It introduces significant latency and network overhead for both the user and the extension's backend. | ||||||
| * **Privacy**: It requires sending the user's browsing-related URLs to a third-party server, which is a privacy concern. | ||||||
|
|
||||||
| This API resolves all three issues by providing the exact, browser-verified information directly. | ||||||
|
|
||||||
| ### Open Web API | ||||||
|
|
||||||
| This proposal extends Web Request API which is not available on Open Web. | ||||||
| However, the Open Web has [Controlled Frame API](https://wicg.github.io/controlled-frame) which includes WebRequest as well. It is planned to extend Controlled Frame API, although in a separate web proposal. | ||||||
|
|
||||||
| ## Implementation Notes | ||||||
|
|
||||||
| This proposal is heavily inspired by Firefox's `webRequest.getSecurityInfo()` to promote cross-browser compatibility. However, it adapts the design to fit Chrome's non-blocking, event-driven architecture (Manifest V3): | ||||||
|
|
||||||
| * **Event-Based vs. Function Call**: Instead of a separate `getSecurityInfo(requestId)` function (as in Firefox), this API injects the data into the existing `onHeadersReceived` event. | ||||||
| * **Rationale**: In a non-blocking model, the internal network and certificate data associated with a request is often discarded immediately after the request pipeline advances. A separate asynchronous getSecurityInfo call would be racy and unreliable. By "opting in" with extraInfoSpec, the extension signals the browser to hold onto this data just long enough to deliver it with the onHeadersReceived event, ensuring data availability without blocking. | ||||||
|
|
||||||
|
|
||||||
| ## Future Work | ||||||
vkrot-cell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| This proposal focuses on the most critical and high-priority data (the leaf certificate). The `SecurityInfo` object can be extended in the future to include: | ||||||
|
|
||||||
| * The full certificate chain (`CertificateInfo[]`). | ||||||
| * Specific TLS parameters (e.g., protocol version, cipher suite). | ||||||
| * Additional properties like `isDomainMismatch` or `isEV`. | ||||||
Uh oh!
There was an error while loading. Please reload this page.