Everyone secures webhook ingestion. Almost nobody talks about SSRF via the delivery worker.
I've been staring at webhook architectures for years, and the security conversation is almost always backwards. We obsess over verifying inbound payloads while leaving the outbound side wide open.
Why Your HMAC Doesn't Save You Here
HMAC verification only protects ingestion, not outbound delivery to tenant URLs. That signature proves the payload came from who it claims. Great.
But your delivery worker — the thing that POSTs events to customer-provided URLs — has a completely different threat model. HMAC doesn't even enter the picture on that side.
Think about it. A tenant registers https://totally-legit-domain.com/webhook as their endpoint. You validate the URL looks fine. You maybe even check it doesn't resolve to a private IP. Then you move on with your life.
DNS Rebinding: The Actual Scary Part
Here's where it gets ugly. DNS rebinding can redirect webhook deliveries to internal IPs like 169.254.169.254.
The attack works like this:
→ Tenant registers a domain they control
→ At registration time, it resolves to a perfectly normal public IP
→ Your validation passes
→ Later, the DNS record flips to 169.254.169.254 (the cloud metadata endpoint)
→ Your delivery worker happily POSTs to it, potentially leaking cloud credentials
Your worker just became a proxy into your own infrastructure. The tenant didn't hack anything. They just gave you a URL and waited. 🎯
This isn't theoretical. Cloud metadata endpoints are the crown jewels. One leaked IAM credential from that 169.254 address and it's game over.
Validate at Delivery Time, Every Time
Private IP blocklists must be checked at delivery time, not just registration time. I can't stress this enough.
Checking the URL once when the tenant sets it up is not sufficient. DNS records change. That's literally what DNS rebinding exploits.
Every single outbound request from your delivery worker needs to:
→ Resolve the hostname fresh
→ Check the resolved IP against a private range blocklist before opening the connection
→ Reject anything pointing to 10.x, 172.16-31.x, 192.168.x, 169.254.x, or localhost
Some HTTP libraries will follow redirects automatically too. A 302 hop to an internal IP is just as dangerous. You need to validate at every step of the chain, not just the initial resolution.
This Is an Architecture Problem, Not a Config Problem
The frustrating part is that most webhook guides treat security as "add HMAC and you're done." That's security theater for the delivery path. 🔒
If you're building a webhook system, the delivery worker is the most dangerous component you own. It makes outbound HTTP requests to attacker-controlled URLs. Read that sentence again.
You're essentially running an HTTP client that takes instructions from your tenants. That deserves the same paranoia you'd give to user-uploaded code execution.
At a high level, the decisions that actually matter:
→ Pin DNS resolution to the moment of delivery and validate the IP
→ Disable HTTP redirects or re-validate after each hop
→ Run delivery workers in a network segment with no access to internal services or metadata endpoints
→ Treat every tenant URL as hostile, every time, forever
The Takeaway
Your inbound webhook security is probably fine. Your outbound delivery worker is probably a loaded footgun pointed at your cloud metadata endpoint. The fix isn't complicated — validate DNS resolution at delivery time, block private IPs, isolate the worker network. But you have to actually do it, and most teams don't because every tutorial stops at HMAC. 😅
What does your webhook delivery pipeline look like — are you validating resolved IPs on every outbound request, or just at registration time?
Top comments (0)