Follow-up to #206. That issue closes the static-record bypass with a resolve-and-validate re-check at every fetch seam. This one tracks the remaining TOCTOU gap: pinning the socket to the IP we already validated.
The gap: even after we resolve a host and validate every returned IP, the real connection resolves DNS again at connect time. An attacker who controls the authoritative resolver can return a public IP for the check and flip to 169.254.169.254 or an RFC-1918 host for the connect, inside the TTL window. That is classic DNS rebinding. The only airtight fix is to make the socket connect to the exact IP we validated instead of re-resolving.
Feasibility is uneven across our fetch tiers, which is why this is a separate, phased issue rather than part of #206:
- HTTP client tier. Pinnable cleanly with a lookup/connect hook on the dispatcher, so the socket uses the validated IP and never re-resolves. Highest value, do this first.
- TLS-impersonation tier. Depends on whether that stack exposes a connect or lookup hook. If it does, mirror the HTTP-tier approach; if not, record the residual.
- Browser tier. Effectively unpinnable, since the browser does its own DNS and we don't control its socket layer. Options are pre-nav resolve-and-validate (check only, does not close rebinding) or routing through a local validating proxy. Whatever we pick, document the residual honestly rather than implying the browser tier is rebinding-safe.
Scope: pin the HTTP client tier first (small, high value), then the TLS tier if its stack allows, then decide the browser tier (proxy vs documented residual). The resolved-IP re-check from #206 stays as the floor everywhere, so even the unpinned tiers still block the static-record case.
Depends on #206 landing first, since the re-check is what produces the validated-IP set to pin to.
@spartan8806 flagging you, since you called this out in #206 and offered to take the pinning follow-up. No pressure to own it, but it's yours if you want it. Same phasing applies: HTTP tier first.
Follow-up to #206. That issue closes the static-record bypass with a resolve-and-validate re-check at every fetch seam. This one tracks the remaining TOCTOU gap: pinning the socket to the IP we already validated.
The gap: even after we resolve a host and validate every returned IP, the real connection resolves DNS again at connect time. An attacker who controls the authoritative resolver can return a public IP for the check and flip to
169.254.169.254or an RFC-1918 host for the connect, inside the TTL window. That is classic DNS rebinding. The only airtight fix is to make the socket connect to the exact IP we validated instead of re-resolving.Feasibility is uneven across our fetch tiers, which is why this is a separate, phased issue rather than part of #206:
Scope: pin the HTTP client tier first (small, high value), then the TLS tier if its stack allows, then decide the browser tier (proxy vs documented residual). The resolved-IP re-check from #206 stays as the floor everywhere, so even the unpinned tiers still block the static-record case.
Depends on #206 landing first, since the re-check is what produces the validated-IP set to pin to.
@spartan8806 flagging you, since you called this out in #206 and offered to take the pinning follow-up. No pressure to own it, but it's yours if you want it. Same phasing applies: HTTP tier first.