Skip to content

Proposal: WebRequest.securityInfo API for receiving TLS certificate from web requests. #882

@vkrot-cell

Description

@vkrot-cell

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 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.

  • 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".

  • 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.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.

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 separate getSecurityInfo() 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", "securityInfoRawDer flags which avoid unnecessary work when the data isn't needed.

Future Work

  • The SecurityInfo can be updated with more fields if necessary, such as isDomainMismatch or full list of server certificates.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions