SSRF Defense Has to Survive DNS, Redirects, and Replay
Validating a webhook destination when it is saved is useful feedback, but the security boundary is the address selected at dial time on every delivery path.
Rejecting a private webhook URL when a user saves it is useful feedback, but it is not the SSRF boundary. DNS can change before delivery, redirects can select a new target, and replay can take a different code path. Signalbin enforces the address policy at connection time and makes every outbound delivery use that client.
This post describes Signalbin's current defense, not a universal drop-in recipe. Network policy depends on the service's legitimate destinations and deployment topology.
Save-time validation catches mistakes early
When a destination is created or updated, Signalbin accepts only HTTP or HTTPS URLs with a hostname. It resolves the host and rejects loopback, private, link-local, unspecified, and cloud metadata addresses. That gives the user an immediate error for targets such as localhost, 10.0.0.5, or 169.254.169.254.
The check is deliberately not trusted forever. A hostname can resolve to a public address while it is saved and a private address later. OWASP's SSRF prevention guidance calls out DNS pinning and rebinding as reasons that domain allowlisting and resolution need careful treatment.
The dialer validates the address it will connect to
Signalbin's outbound transport uses a custom DialContext. It splits the requested host and port, resolves the host, checks every returned IP against the address policy, then dials an approved resolved address directly.
That order closes the time-of-check gap inside the delivery attempt. The system does not validate one DNS answer and then hand the hostname to a second resolver that might return something else.
Checking all returned addresses also matters. Accepting a hostname because one answer is public while another is private leaves selection to resolver ordering or connection fallback. Signalbin rejects the target if any returned address violates the policy.
Redirects are a new request
Go's http.Client follows redirects by default and exposes policy through 1. A destination that begins on a public host can redirect to an internal address, so validating only the original URL is insufficient.
Signalbin refuses redirects for relay delivery. That is stricter than revalidating every hop, but it makes the delivery target stable and avoids forwarding a webhook body and sensitive headers to a URL the user did not configure.
Replay must use the same guarded client
Live fan-out and manual replay both send stored, user-influenced requests. If replay creates a plain http.Client, the product has an SSRF bypass even though ordinary delivery is protected.
Signalbin routes both paths through the same client factory, so the dial-time checks, redirect policy, timeout, and relay marker apply consistently. Tests cover direct private targets, deferred DNS resolution, redirect behavior, and a delivery path that never called the save-time validator.
That last test is important because it proves the enforcement point is the transport. Controller validation improves errors. The dialer protects the network.
SSRF defenses tend to fail between parsing and dialing, between the first request and a redirect, or between live delivery and replay. Signalbin keeps one outbound transport with a narrow address policy, then makes bypassing it harder than using it.