PHP security explainer · defensive
SSRF in PHP: what it is and how to fix it
Features such as URL previews, webhooks, image fetchers and PDF generators make the server fetch a URL. If the visitor controls that URL, they can aim your server at places they cannot reach themselves.
Last updated
What it is
Your server usually sits inside a trusted network. A request it makes can reach databases, admin panels, other microservices and cloud instance metadata services that hand out credentials. SSRF turns any fetch-a-URL feature into a proxy into that network. MITRE classifies it as CWE-918, and OWASP keeps a dedicated prevention cheat sheet.
How to detect it
- Search for
curl_setopt(... CURLOPT_URL ...),file_get_contents($var),fopen($var),get_headers()and HTTP client calls such as Guzzle that take a URL from input. - List every feature that fetches remote content: webhooks, avatar or image import, link previews, feed readers, PDF or screenshot rendering, XML parsing with external entities.
- Review outbound traffic from the web server; unexpected requests to
169.254.169.254,localhostor private ranges are a red flag.
How to fix it
- Allowlist the hosts and schemes the feature really needs (usually only
https) and reject everything else. - Resolve the host yourself and refuse private, loopback and link-local addresses, then connect to that resolved IP so DNS cannot change between check and use.
- Disable redirect following (
CURLOPT_FOLLOWLOCATIONfalse) and limit protocols withCURLOPT_PROTOCOLS. - On cloud hosts, require session tokens for instance metadata (for example AWS IMDSv2) and restrict egress with a firewall.
Read-only checks and safe patterns
# Find server-side fetches that may take a user-supplied URL (read-only)
grep -rnE "(curl_setopt.*CURLOPT_URL|file_get_contents\s*\(\s*\$|fopen\s*\(\s*\$)" --include=*.php . | grep -v /vendor/
// Allowlist hosts and schemes before fetching
$u = parse_url($url);
$allowed = ['api.example.com'];
if (($u['scheme'] ?? '') !== 'https' || !in_array($u['host'] ?? '', $allowed, true)) {
throw new RuntimeException('URL not allowed');
}
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);Is a blocklist of 127.0.0.1 enough?
No. Alternative IP notations, IPv6, DNS names that resolve to private addresses and redirects all bypass simple blocklists. OWASP recommends allowlists.
Does a WAF stop SSRF?
It can catch some patterns but cannot know which internal hosts matter to you. Fix it in the code.
Other PHP vulnerability classes
- PHP object injection: what it is and how to fix itPHP object injection happens when user-controlled input reaches unserialize(), which can instantiate objects whose magic methods run attacker-chosen logic.
- PHP file inclusion (LFI/RFI): what it is and how to fix itFile inclusion happens when user input controls the path passed to include or require, letting an attacker read local files (LFI) or, with allow_url_include on, run remote code (RFI).
- XSS in PHP: what it is and how to prevent itCross-site scripting (XSS) happens when a PHP page outputs user-controlled data into HTML without escaping, so an attacker’s script runs in other visitors’ browsers.
- phpinfo() exposure: why it matters and how to fix itA public phpinfo() page reveals the exact PHP version, loaded modules, file paths and often environment variables or secrets, which helps attackers target bugs such as PHP2Shell.
Sources
Outbound links leave this site. We do not host exploit code.
- CWE-918: Server-Side Request Forgery (SSRF) — MITRE CWE
- Server-Side Request Forgery Prevention Cheat Sheet — OWASP Cheat Sheet Series