flawopen.com/ssrf/Php

● CWE-918 · Haute
Recherche · FlawOpen

Server-Side Request Forgery in PHP

How curl_exec in PHP exposes internal networks, and how to filter IPs using FILTER_VALIDATE_IP and CURLOPT_FOLLOWLOCATION.

💡 Explication en Langage Simple (ELI5)

Imaginez envoyer un coursier chercher un colis dans une boutique publique, mais en lui donnant l'adresse du coffre-fort du bureau de la direction. Comme le coursier possède un badge d'accès interne, il ouvre le coffre et vous remet les secrets de l'entreprise.

Concepts Clés et Termes

Web Application Security
Composant d'architecture clé affecté par CWE-918.
CWE-918
Classification standard Common Weakness Enumeration (CWE) pour ssrf-php.
Defense-in-Depth
Vérification d'ingénierie multicouche et isolation des limites à l'exécution.

Déroulement de l'Attaque Étape par Étape

Step 1

Réception d'une URL Distante Non Fiable

L'application prend en charge une URL externe pour récupérer un avatar, un webhook ou convertir un document.

Step 2

Adressage de Réseau Interne

L'attaquant fournit une URL ciblant les métadonnées cloud (ex. http://169.254.169.254/latest/meta-data/) ou une IP privée de bouclage.

Step 3

Émission de Socket Non Filtrée

Le client HTTP du serveur émet une requête depuis le réseau privé sans résoudre au préalable l'adresse IP cible.

Step 4

Exfiltration des Identifiants Cloud

Le service de métadonnées interne fait confiance au serveur et renvoie les clés IAM temporaires ou les jetons d'accès.

Code Source : Vulnérable vs Sécurisé

✕ IMPLÉMENTATION VULNÉRABLE
<?php
// VULNERABLE: file_get_contents on user-controlled URL
$url = $_GET['url'];

// Attacker input: http://169.254.169.254/latest/meta-data/
$content = file_get_contents($url);

echo $content;
?>
✓ PATCH SÉCURISÉ ET ROBUSTE
<?php
// HARDENED: Parse URL, resolve DNS, filter private IPs, and disable redirects
$url = $_GET['url'] ?? '';

$parsed = parse_url($url);
if (!$parsed || !in_array(strtolower($parsed['scheme'] ?? ''), ['http', 'https'], true)) {
    http_response_code(400);
    exit('Invalid URL scheme');
}

$host = $parsed['host'] ?? '';
$ips = gethostbynamel($host);

if (!$ips) {
    http_response_code(400);
    exit('Unable to resolve host');
}

// Check every resolved IP address
foreach ($ips as $ip) {
    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
        http_response_code(403);
        exit('Access to private/internal address denied');
    }
}

// Fetch using cURL with redirects disabled
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false, // Prevent redirect bypasses
    CURLOPT_TIMEOUT        => 3,
    CURLOPT_PROTOCOLS      => CURLPROTO_HTTP | CURLPROTO_HTTPS,
]);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
?>

Liste de Contrôle de Sécurité pour l'Ingénierie

References