HookGet Open dashboard

Webhooks and SSRF

Why a delivery service is an SSRF machine by construction, and how to contain it.

In short

  • A webhook service makes HTTP requests to addresses supplied by its users — that is SSRF by construction.
  • Resolving a name and then connecting by name allows the answer to change in between.
  • HookGet resolves, validates every returned address, then connects to the exact validated address.
  • A second, independent layer refuses the same traffic at the network level.

Why this is the centre of the product

Delivery means fetching a URL a customer typed. If that URL resolves to something inside the network — a metadata service, a database, an internal admin panel — the service becomes a proxy into its own infrastructure on behalf of whoever typed it. This is not an edge case; it is the normal operation of the product, pointed slightly differently.

The check that actually holds

StepWhy it is in this order
Parse the URL strictlyRefuse credentials in the URL, non-HTTP schemes and ports outside the allowlist
Resolve the hostnameGet every address the name currently answers with
Validate every addressOne private address among several is enough to refuse
Connect to the validated addressNot to the name — otherwise a second lookup can return something else
Keep the original hostname for TLS and HostSo certificate validation and virtual hosting still work
Never follow redirectsA redirect is the destination choosing a second address after the check

The subtle one is step four. Validating a name and then handing the name to the HTTP client leaves a window in which DNS can answer differently — the rebinding attack. Pinning the connection to the address that was actually checked closes it.

Two layers, not one

Code can have bugs, so the network is configured to refuse the same traffic independently: the service account cannot reach private ranges or cloud metadata addresses at all. Either layer alone would be a single point of failure for the most serious class of bug in this kind of product.

If you run your own

  • Validate on the resolved address, not the string.
  • Check every address the name returns, including IPv6 and IPv4-mapped forms.
  • Refuse redirects, or re-run the whole check on the new location.
  • Do not make the exemption for local development available in production. Ours is inert when the environment says production, and there is a test that asserts it.