Blog / Engineering
SSRF and why an admin-typed webhook or integration URL is a bigger risk than it looks
Server-side request forgery sounds like an exotic vulnerability class, and the mechanism behind it is almost embarrassingly simple: if a system lets a user configure a URL that the server itself will later fetch — a webhook destination, an integration base URL, a callback endpoint — and that system doesn't restrict what the URL can point to, an attacker with access to that config field can point it at internal infrastructure the server can reach but the attacker can't reach directly. The request comes from inside the network, wearing the server's own credentials and trust.
The reason this shows up specifically in integration and webhook config fields, rather than everywhere, is that those fields exist precisely to let an admin type in an arbitrary destination — that's the whole feature. A form field that's supposed to accept 'any URL the customer wants' is structurally the hardest kind of input to restrict, because restricting it too aggressively breaks the legitimate use case, and restricting it too loosely leaves the SSRF path wide open.
What an unguarded webhook URL field can actually reach
- Cloud metadata endpoints — many cloud providers expose an internal-only HTTP endpoint that hands out credentials to whatever's running on that instance; a server that will fetch any URL an admin provides can be tricked into fetching that endpoint and leaking those credentials back through the response.
- Internal services with no auth of their own, because they were built assuming only other internal services could ever reach them — an admin panel, an internal API, a database's management interface.
- Localhost and loopback addresses, DNS names that resolve to private IP ranges, or redirects that point somewhere different from the URL that was originally validated — each of those defeats a naive 'check the URL once at input time' defense.
That last point is the one that trips up otherwise-careful implementations: validating a URL's hostname at the moment it's saved doesn't protect against DNS rebinding, where the hostname resolves somewhere safe at validation time and somewhere internal at request time, or against a redirect chain that starts at an approved destination and ends somewhere else entirely. A real defense has to guard the request at the moment it's actually made, resolve the destination at that moment, and reject anything that lands in a private or reserved address range — not just check the string the admin typed once and trust it forever after.
The practical design pattern that holds up: route every outbound request to an admin-configured host through a single guarded path that resolves and validates the destination at request time, and treat a hardcoded, literal vendor endpoint in your own code — one no admin can ever change — as the only category allowed to skip that guard, because it's not attacker-influenced input. Anything an admin can type into a config field, no exceptions, goes through the guard. This is a real architectural rule in Nexus's own codebase, enforced at every route that touches an admin-set host, not a policy written down and hoped for. If you're evaluating a vendor's webhook or integration feature, ask them directly whether an admin-typed URL can reach an internal address, and ask them to show you what happens when you point a test webhook at a loopback address — a vendor with a real guard will show you the request getting rejected, not explain why it's fine.