Skip to content
← All packages

SSRF Guard for Laravel

A hardened, config-driven guard against server-side request forgery (SSRF) for outbound URLs in Laravel. Validates any URL your app is about to fetch and refuses private, reserved, or cloud-metadata addresses.

SSRF Guard for Laravel validates any URL your application is about to fetch, a webhook endpoint, an avatar or import URL, an OAuth callback, and refuses anything that points at a private, reserved, or cloud-metadata address. It resolves DNS once, pins the connection to that exact IP, and will not follow redirects, then behaves like the normal HTTP client.

One line to guard a request

Http::ssrf($url)->post($url, $payload) validates the URL, pins the connection to the resolved public IP, refuses redirects, and otherwise behaves like the normal Laravel HTTP client. It throws Cbox\Ssrf\Exceptions\BlockedUrl when the target is unsafe.

Why a correct SSRF guard is hard

An attacker who controls a URL your server fetches can reach your internal network: 169.254.169.254 for cloud credentials, 127.0.0.1:6379 for Redis, 10.0.0.5/admin for an internal panel. Most home-grown checks miss DNS rebinding, redirect hops, or IPv6 transition forms. This package handles all three, and is honest about the one thing application code cannot fully solve.

What It Defends Against

DNS Rebinding (TOCTOU)

Resolves the host once and pins the connection to that exact IP via CURLOPT_RESOLVE, keeping TLS and SNI intact, with a post-connection check that the socket actually connected to a validated address.

Redirect-Based Bypass

Refuses to follow redirects on guarded requests, so a 30x response to an internal host cannot smuggle the request onward.

Alternate IP Encodings

Decimal (2130706433) and hex (0x7f000001) IPv4 literals are normalized before validation, closing the browser-redirect encoding tricks.

IPv6 Transition Forms

IPv4-mapped, IPv4-compatible, 6to4, NAT64 and Teredo addresses are blocked wholesale, and their embedded IPv4 is extracted and re-checked. This is the Symfony CVE-2026-48736 class of bypass.

Cloud Metadata

AWS, GCP and Azure 169.254.169.254, AWS IMDSv2 IPv6 fd00:ec2::254, Alibaba, Oracle, and the metadata.google.internal name are all refused.

Reserved Ranges & URL Tricks

Loopback, RFC 1918, link-local, ULA, CGNAT, TEST-NET and multicast for both IPv4 and IPv6, plus embedded credentials, blocked host suffixes, and non-http(s) schemes.

Three ways to use it

Guard an outbound request with Http::ssrf($url). Validate user-supplied URLs at the form boundary with the PublicUrl validation rule. Or resolve the UrlGuard contract directly and call assertSafe(), isSafe() or pinnedOptions() to guard a request from your own HTTP client. Browser-redirect targets like OAuth authorize URLs are validated without a DNS lookup via assertSafeRedirect(), since the browser resolves them.

The full policy lives in config/ssrf.php: allowed_schemes, blocked_hosts, blocked_host_suffixes, blocked_ips, blocked_cidrs, pin_dns, and a master enforce switch. A single-tenant or on-prem install that must reach internal hosts can set SSRF_ENFORCE=false, and the scheme, credential, and blocked-host checks still run.

Defense in depth, not a complete fix

A network egress allow-list is the real answer: a firewall, security-group or egress proxy that can only reach approved destinations closes SSRF completely. Use this guard and restrict egress, block 169.254.169.254 at the network, and require IMDSv2. An HTTP proxy defeats connection pinning, and response content is out of scope.

Deterministic in tests

Bind the FakeResolver to make DNS deterministic in your own tests, mapping a hostname to whatever address you want to assert against, so you can prove your guard blocks evil.test resolving to 169.254.169.254 without touching the network.