SSRF and webhooks: why destination URL validation matters
A webhook destination URL is, from the server's point of view, user-controlled input to an outbound HTTP request. That's the exact shape of a server-side request forgery (SSRF) vulnerability: if nothing stops the URL from pointing at 169.254.169.254 (a cloud provider's instance metadata endpoint), localhost, or an internal service on a private subnet, the webhook platform becomes a proxy an attacker can use to reach infrastructure it was never supposed to touch.
Why this is specifically a webhook-platform problem
Most SSRF writeups are about a single feature — an image-fetching endpoint, a URL preview, a "test this webhook" button. A webhook delivery platform has the same exposure on every single dispatch, indefinitely: a destination URL that was safe when the endpoint was created can later be edited, and every future delivery re-resolves and re-sends to whatever the URL now points at. A one-time check at creation isn't enough.
What validation actually needs to check
A destination URL needs to be rejected if it resolves to a private, loopback, or otherwise disallowed address — and that check needs to run more than once:
- On create — a request to add an endpoint pointing at a private address should fail immediately, not silently succeed and fail later.
- On edit — a destination can be changed after creation. If the check only ran at creation, editing it to a private address afterward would bypass validation entirely.
- On dispatch — DNS is not static. A hostname that resolved to a public IP at creation time can be repointed at a private one later (DNS rebinding), so the safest implementations re-validate at the moment of the actual outbound request, not just when the URL was first saved.
This is exactly how Jitterflow validates destinationUrl: on every create, edit, and dispatch, against private and loopback ranges — see Endpoints. A destination that fails this check on create or edit returns 422; if a previously-valid destination is later edited to fail the check between edits, the next ingest call returns 422 too rather than silently attempting delivery.
Tunnel domains are a separate, narrower problem
Local development tunnels (ngrok.io, localtunnel.me, serveo.net, and similar) are public URLs, so they pass a pure private-IP check — but they route straight back to whatever's running on someone's laptop or a temporary dev box, which is a different kind of risk on a shared, multi-tenant platform. Jitterflow's free Developer tier additionally can't point at unverified tunnel domains; paid tiers can, since they've gone through billing verification. A tunnel destination on a restricted tier returns 403, distinct from the 422 a private-IP destination gets.
Full reference
Every endpoint field, its default, and every error code (400, 403, 422) is on Endpoints.