Security News

Cybersecurity news aggregator

🔓
HIGH Vulnerabilities Web Discovery

PHP Server-Side Request Forgery (SSRF) | Security Vulnerability Database | Sourcery

This article details a high-risk Server-Side Request Forgery (SSRF) vulnerability
Read Full Article →

PHP Server-Side Request Forgery (SSRF) High Risk Server-Side Request Forgery php ssrf http-requests url-validation internal-services What it is The PHP application makes HTTP requests to URLs controlled by user input without proper validation, enabling Server-Side Request Forgery attacks. Attackers can exploit this to access internal services, cloud metadata endpoints, or perform port scanning on internal networks. Language: // Vulnerable: Direct user input in HTTP requests function fetchUrl($url) { // Dangerous: User-controlled URL without validation $context = stream_context_create([ 'http' => [ 'timeout' => 10 ] ]); $content = file_get_contents($url, false, $context); return $content; } // Alternative vulnerable pattern with cURL function fetchUrlCurl($url) { $ch = curl_init(); // Dangerous: Direct use of user input curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $response = curl_exec($ch); curl_close($ch); return $response; } // Usage allowing SSRF $userUrl = $_GET['url']; // Could be http://localhost/admin or http://169.254.169.254/ $content = fetchUrl($userUrl); // Secure: URL validation and allowlisting class SecureHttpClient { private $allowedDomains = [ 'api.example.com', 'trusted-service.com', 'safe-api.org' ]; private $blockedIpRanges = [ '127.0.0.0/8', // Localhost '10.0.0.0/8', // Private network '172.16.0.0/12', // Private network '192.168.0.0/16', // Private network '169.254.0.0/16', // Link-local (AWS metadata) '::1/128', // IPv6 localhost 'fc00::/7' // IPv6 private ]; public function fetchUrl($url) { // Validate URL format if (!filter_var($url, FILTER_VALIDATE_URL)) { throw new InvalidArgumentException('Invalid URL format'); } $parsedUrl = parse_url($url); // Validate scheme if (!in_array($parsedUrl['scheme'], ['http', 'https'], true)) { throw new InvalidArgumentException('Only HTTP/HTTPS schemes allowed'); } // Validate domain against allowlist if (!in_array($parsedUrl['host'], $this->allowedDomains, true)) { throw new InvalidArgumentException('Domain not in allowlist'); } // Resolve and validate IP address $ip = gethostbyname($parsedUrl['host']); if ($this->isBlockedIp($ip)) { throw new InvalidArgumentException('IP address is blocked'); } // Make secure request return $this->makeRequest($url); } private function isBlockedIp($ip) { foreach ($this->blockedIpRanges as $range) { if ($this->ipInRange($ip, $range)) { return true; } } return false; } private function ipInRange($ip, $range) { list($subnet, $mask) = explode('/', $range); return (ip2long($ip) & ~((1 << (32 - $mask)) - 1)) === ip2long($subnet); } private function makeRequest($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_MAXREDIRS, 3); curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); curl_setopt($ch, CURLOPT_USERAGENT, 'SecureApp/1.0'); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_error($ch)) { $error = curl_error($ch); curl_close($ch); throw new Exception('HTTP request failed: ' . $error); } curl_close($ch); if ($httpCode >= 400) { throw new Exception('HTTP request failed with status: ' . $httpCode); } return $response; } } // Secure usage try { $client = new SecureHttpClient(); $userUrl = filter_input(INPUT_GET, 'url', FILTER_SANITIZE_URL); if ($userUrl) { $content = $client->fetchUrl($userUrl); } } catch (Exception $e) { error_log('HTTP request error: ' . $e->getMessage()); // Handle error appropriately } 💡 Why This Fix Works See fix suggestions for detailed explanation. Why it happens PHP applications accept user-supplied URLs and directly pass them to HTTP client functions like file_get_contents($_GET['url']), curl_setopt($ch, CURLOPT_URL, $_POST['url']), or HTTP client libraries (Guzzle, HTTPlug) without validation. Common vulnerable patterns include image proxy services: file_get_contents($_GET['image_url']) intended to fetch external images but exploitable to access internal services, webhook implementations accepting callback URLs: curl_exec(curl_setopt_array($ch, [CURLOPT_URL => $webhook_url])) allowing attackers to specify internal endpoints, RSS feed readers fetching user-provided feed URLs: file_get_contents($_POST['feed_url']) enabling access to internal APIs, URL shortener expansions following user-submitted short URLs to retrieve final destinations, and PDF generators rendering user-supplied URLs into documents. The vulnerability enables multiple attack vectors: accessing cloud metadata endpoints (http://169.254.169.254/latest/meta-data/ on AWS, http://metadata.google.internal/computeMetadata/v1/ on GCP) extracting API credentials and configuration, port scanning internal networks by observing response times or error messages reveal

Share this article