# TLS handshake state trusted early

Security decisions made on handshake events before async checks finish or before transcript binding is proven

_Area: Web authentication and account takeover. Seen in 2 disclosed reports; latest 2026-07-22. Updated 2026-10-25._

## How it works
A security decision is made from a handshake event that does not yet prove what the code assumes. Either the event fires before an asynchronous check (OCSP, revocation) resolves, or the binding between a handshake and its transcript is weaker than the code believes, so a spliced or retried connection is treated as the authenticated one. The broken assumption: "handshake completed" means "this connection is the one that was validated."

## Where it shows up
- Handshake-completion events forwarded to downstream handlers before an async validation (OCSP, revocation, identity) returns [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-wc96-39fc-566f.md)
- Security bound to a transcript hash or renegotiation counter that an abbreviated/resumption handshake can be spliced into [E1](https://bugbountylibrary.com/r/h1%3A7277.md)
- Retry/fallback paths (autoSelectFamily, happy-eyeballs, reconnect) that carry hooks or callbacks from a discarded socket handle [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-chqv-56wv-7564.md)
- Client-certificate auth where the server trusts the connection identity without binding it to the full handshake sequence, so a spliced resumption makes two connections look identical [E1](https://bugbountylibrary.com/r/h1%3A7277.md)

## How to review
Trace the ordering: does any success event reach a handler that reads or sends data before the async check resolves? The invariant: no downstream handler may act on handshake success until every validation it depends on has completed and passed. For transcript-bound schemes, check that two distinct connections cannot produce identical transcripts via an inserted resumption. For retry paths, confirm every hook is rebound to the new handle, not the failed one.

## Variants seen
- Async OCSP check racing handshake-completion event; channel closed only after REVOKED returns [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-wc96-39fc-566f.md)
- Triple handshake: resumption spliced between two full handshakes defeats RFC5746 binding [E1](https://bugbountylibrary.com/r/h1%3A7277.md)
- Stale TLS upgrade hook on retry leaves fallback socket plaintext [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-chqv-56wv-7564.md)

## Impact and severity
Revoked servers received client data [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-wc96-39fc-566f.md); MITM authenticated as victim client [E1](https://bugbountylibrary.com/r/h1%3A7277.md); tokens and card data leaked in plaintext [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-chqv-56wv-7564.md). Severity is high because the failure is silent: the connection looks established and validated. Worse when the check is asynchronous (ordering window) or when retries are common (fallback paths).

## Why fixes fail
Closing the channel after the async result [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-wc96-39fc-566f.md) still lets data flow during the window; the event must be gated, not the close deferred. Binding to a single-connection counter [E1](https://bugbountylibrary.com/r/h1%3A7277.md) fails once resumption is spliced in, because the RI extension only covers renegotiation on one connection and cannot distinguish the spliced connection.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A7277.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-wc96-39fc-566f.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-chqv-56wv-7564.md
