Skip to content

security: prevent SSRF DNS rebinding by dialing validated IP directly (#3)#582

Merged
lakhansamani merged 1 commit into
mainfrom
security/ssrf-toctou
Apr 7, 2026
Merged

security: prevent SSRF DNS rebinding by dialing validated IP directly (#3)#582
lakhansamani merged 1 commit into
mainfrom
security/ssrf-toctou

Conversation

@lakhansamani
Copy link
Copy Markdown
Contributor

Summary

Closes a TOCTOU / DNS rebinding hole in webhook endpoint validation. ValidateEndpointURL resolved DNS during validation but http.NewRequest re-resolved when dialing — an attacker controlling DNS could return a public IP for validation and a private IP (e.g. 169.254.169.254) for the actual dial.

Changes

  • New validators.SafeHTTPClient(ctx, rawURL, timeout) that resolves the host once, rejects private/loopback/link-local IPs, and pins http.Transport.DialContext to the validated IP. TLS still uses ServerName=host so SNI and cert validation work normally.
  • internal/graphql/test_endpoint.go and internal/events/events.go now use SafeHTTPClient instead of constructing a fresh http.Client.
  • Save-time ValidateEndpointURL callers (add_webhook.go, update_webhook.go) keep the existing format check — they don't make outbound HTTP.

Test plan

  • go build ./...
  • go test ./internal/validators/...
  • Manual: webhook test endpoint with public host succeeds
  • Manual: webhook test endpoint with private host (http://169.254.169.254/) is rejected
…#3)

ValidateEndpointURL() resolved DNS during validation, but http.NewRequest
re-resolved the hostname when actually dialing. An attacker controlling
authoritative DNS could return a public IP for the validation lookup and a
private IP (e.g. 169.254.169.254 cloud metadata) for the dial — a TOCTOU
DNS rebinding bypass.

Add validators.SafeHTTPClient that resolves the host once, rejects any
private/loopback/link-local IPs, and pins http.Transport.DialContext to
the validated IP. TLS still uses ServerName=host so SNI and certificate
validation work normally.

Updated callers:
- internal/graphql/test_endpoint.go (admin webhook test)
- internal/events/events.go (webhook dispatcher)

Save-time validators (add_webhook.go, update_webhook.go) keep using
ValidateEndpointURL since they don't make outbound HTTP requests.
@lakhansamani lakhansamani merged commit 5d8eaff into main Apr 7, 2026
@lakhansamani lakhansamani deleted the security/ssrf-toctou branch April 7, 2026 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant