-
Notifications
You must be signed in to change notification settings - Fork 73
Description
Summary
An API proposal to extend the webRequest API to provide TLS/QUIC certificate information for a given request.
Objective
This API enables extensions to inspect the server certificate of a web request after a secure connection has been established. This fulfills a long-standing developer need for TLS introspection, allowing for the creation of advanced security and debugging tools.
Currently, Firefox has a similar getSecurityInfo extensions API, so this proposal mostly targets other browsers.
Related Issues:
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.
Schema
This API adds a new optional field, securityInfo, to the details object of the onHeadersReceived.
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 {
/**
* SHA-256 hash of the certificate.
*/
sha256: string;
}
export interface CertificateInfo {
fingerprint: Fingerprint;
rawDER: Uint8Array;
}
/**
* 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 {
/**
* The array contains a single CertificateInfo object, for the server certificate, or empty in case
* the connection state is "insecure" (http).
*/
certificates: CertificateInfo[];
/**
* State of the connection.
*/
state: ConnectionState;
}
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
onHeadersReceivedOptionsare 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. -
A new
securityInfoobject can be obtained in theonHeadersReceivedevent listener. -
To receive this information, an extension must include
"securityInfo"or"securityInfoRawDer"in theextraInfoSpecarray when callingaddListener. This opt-in design prevents performance overhead for the majority of extensions that don't need this data. -
The
securityInfoobject 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 withstate = "broken". -
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 is not included in getSecurityInfo options. -
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.
-
The
CertificateInfo.rawDERfield 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 ifextraInfoSpecarray 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.
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"]
);Implementation Notes
This API is designed with Firefox's webRequest.getSecurityInfo() in mind to promote cross-browser compatibility for extension developers where possible. However, there are key design differences:
- Asynchronous & Non-Blocking: Unlike Firefox's API, which is tied to blocking web requests, this proposal is fully compatible with Chrome's asynchronous, non-blocking model in Manifest V3.
A separategetSecurityInfo()function is not be possible in Asynchronous context, because internal request data would be cleaned up after it is completed.
For that reason there is opt-in"securityInfo","securityInfoRawDerflags which avoid unnecessary work when the data isn't needed.
Future Work
- The
SecurityInfocan be updated with more fields if necessary, such asisDomainMismatchor full list of server certificates.