v147 · Isolated Web Apps · demo

Certificate Flow

How an Isolated Web App uses SecurityInfo from a ControlledFrame HTTPS request to authenticate a parallel Direct Sockets connection to the same server — providing certificate pinning without a custom PKI.

This flow requires Isolated Web Apps on ChromeOS. The diagram explains the pattern; live execution is not possible in a regular browser tab.

actors

IWA (Isolated Web App)
ControlledFrame
Direct Socket (TCP/UDP)
Remote Server
Two-connection pattern — step by step
1
Open a ControlledFrame and navigate to the target origin

The IWA embeds a <controlledframe> element and navigates it to an HTTPS URL on the remote server. This initiates a browser-verified TLS handshake through Chrome's network stack.

<controlledframe src="https://server.example/">
2
Register a webRequest listener requesting securityInfo

Before navigating, the IWA registers a webRequest.onHeadersReceived listener with 'securityInfo' in the extraInfoSpec. Chrome populates details.securityInfo with the browser-verified certificate data.

frame.webRequest.onHeadersReceived.addListener(handler, filter, ['securityInfo'])
3
Extract SecurityInfo.certificates[0].fingerprint.sha256

The listener receives the securityInfo object with the verified leaf certificate fingerprint. This fingerprint is exactly what Chrome confirmed via its root trust store — it is not attacker-supplied.

details.securityInfo.certificates[0].fingerprint.sha256
4
Open a Direct Socket to the same host

The IWA opens a raw TCP/TLS connection to the server using the Direct Sockets API. The TLS handshake in the raw socket exposes the server's certificate independently of the browser's trust store.

new TCPSocket(host, port, { sslEnabled: true })
5
Compare fingerprints — pin the Direct Socket to the verified cert

Hash the certificate from the Direct Socket TLS handshake and compare it to the fingerprint extracted in step 3. If they match, the IWA can be confident the raw connection reaches the same server Chrome has already authenticated.

if (rawCertSha256 === details.securityInfo.certificates[0].fingerprint.sha256) {…}

before and after Chrome 147

Before
No trust anchor for raw connections

An IWA opening a Direct Socket to a custom-protocol server had no way to verify the TLS certificate independently. It either had to ship its own trust store, use out-of-band key pinning, or accept the risk of connecting to an unauthenticated host.

// No way to get browser-verified cert fingerprint
// Must ship own CA or trust blindly
const s = new TCPSocket(host, port, { sslEnabled: true });
// ??? how to verify s is the right server?
Chrome 147+
Browser-verified cert as trust anchor

By routing an HTTPS request through ControlledFrame and reading SecurityInfo, the IWA gets a fingerprint from Chrome's verified TLS session. Any raw socket that presents the same certificate is pinned to the same verified identity.

frame.webRequest.onHeadersReceived.addListener(
  (d) => {
    const knownFp = d.securityInfo.certificates[0].fingerprint.sha256;
    const s = new TCPSocket(host, port, { sslEnabled: true });
    // verify s presents same cert as knownFp
  },
  filter, ['securityInfo']
);

failure branch

If securityInfo is omitted from extraInfoSpec, the request still completes but no browser-verified certificate fingerprint is available. The IWA must block or retry the HTTPS preflight before opening a Direct Socket.

Missing extraInfoSpec
No trust anchor
frame.webRequest.onHeadersReceived.addListener(
  d => {
    if (!d.securityInfo) throw new Error('retry with securityInfo');
  },
  filter, ['responseHeaders']
);
Broken TLS
Do not pin
if (d.securityInfo.state !== 'secure') {
  blockDirectSocket(host);
}

see also

scenario focus

Select a scenario to focus its rendered example and summary.