# SSRF revalidation gap and missing validators

URL checked once then fetched later, or a sibling write path with no check at all

_Area: Web infrastructure and server-side requests. Seen in 4 disclosed reports; latest 2026-08-18. Updated 2026-10-24._

## How it works
A URL is validated once — at creation, first use, or config write — and then trusted forever. The fetch happens later through a different code path that resolves the hostname again, or follows URLs the remote server chooses. The assumption that breaks: "validated once" is treated as "safe always." Between check and connect, DNS can change (rebinding), config can be mutated by another write path, or the remote peer can hand back new URLs. The validator's verdict no longer describes what the client actually connects to. A related shape: a second API surface writes the same model but never runs the validator at all [E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md).

## Where it shows up
- A validator resolves the host, but the HTTP client (httpx, requests, headless browser) resolves independently at connect time [E1](https://bugbountylibrary.com/r/h1%3A530974.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md).
- Allowlist check present in a create handler but absent in the update/PUT path that stores the same field [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md).
- Server-side fetcher renders attacker HTML in a real browser, keeping the page alive so it re-resolves [E1](https://bugbountylibrary.com/r/h1%3A530974.md).
- Protocol clients (ACME/RFC 8555) that follow server-supplied directory/order/finalize URLs [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md).
- A compat/legacy API (e.g. Subsonic) writing the same model with weaker validator rules than the primary API [E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md).
- Redirect handling: per-hop re-validation added to one fetcher but not its siblings [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md).

## How to review
Trace every write path that can set or mutate the URL/config, not just creation [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md)[E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md). Diff validator rules between sibling API surfaces that write the same model [E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md). At the sink, confirm the validated IP is pinned into the actual connection (custom resolver, IP literal, or connect-by-IP with Host header) rather than re-resolved [E1](https://bugbountylibrary.com/r/h1%3A530974.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md). For browser fetchers, check whether the page can outlive the check and re-resolve [E1](https://bugbountylibrary.com/r/h1%3A530974.md). For protocol clients, confirm outbound hosts are pinned to config, not peer-supplied URLs [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md). Enumerate every outbound call site, not just the patched one [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md). Invariant: the address the client connects to must equal the address the validator approved, on every fetch.

## Variants seen
- DNS rebinding against a headless-browser fetcher reaching link-local metadata [E1](https://bugbountylibrary.com/r/h1%3A530974.md).
- Allowlist enforced only at creation; update path stores options verbatim, client follows peer URLs [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md).
- Per-hop redirect validation in one fetcher; sibling fetchers follow cross-host 302s unchecked [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md).
- Parallel API surface (Subsonic radio) missing SafeUrl rules, full-read SSRF via stream proxy [E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md).

## Why fixes fail
A one-time check or create-only allowlist leaves the update path and connect-time resolution unguarded [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md). A per-hop callback applied at one call site leaves siblings open [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md). The concrete remedy is IP pinning at the sink plus a shared validator used by every write path and API surface [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md)[E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md).

## Impact and severity
Reached cloud metadata, leaking SSH keys and service-account tokens [E1](https://bugbountylibrary.com/r/h1%3A530974.md); JWS-signed SSRF to internal URLs [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md); blind access to internal services and IAM credential leaks [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md); full-read SSRF echoing internal responses [E4](https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md). Severity rises with metadata reachability, full-read response echo, and fetchers that follow arbitrary redirects.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A530974.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-xpmj-wjcp-6pww.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-6qvr-wjmv-v8mm.md
- E4: https://bugbountylibrary.com/r/ghsa%3AGHSA-6p96-cfg5-4vhp.md
