flawopen.com/ssrf/Php

● CWE-918 · Alta
Pesquisa · 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.

💡 Explicação em Linguagem Simples (ELI5)

Imagine enviar um assistente de escritório para buscar um pacote em uma loja pública, mas fornecendo o endereço do cofre trancado na sala da gerência. Como o assistente tem um crachá interno, ele abre o cofre e entrega segredos da empresa.

Conceitos Centrais e Termos

Web Application Security
Componente de arquitetura principal afetado pelo CWE-918.
CWE-918
Classificação padrão Common Weakness Enumeration (CWE) para ssrf-php.
Defense-in-Depth
Verificação de engenharia em múltiplas camadas e isolamento de limites em tempo de execução.

Fluxo de Ataque Passo a Paso

Step 1

Ingestão de URL Remota Não Confiável

O aplicativo recebe uma URL de usuário para baixar webhooks, buscar avatares ou gerar relatórios PDF.

Step 2

Endereçamento de Rede Interna

O invasor fornece um endereço local ou do serviço de metadados de nuvem (ex: http://169.254.169.254/latest/meta-data/).

Step 3

Egress de Soquete Irrestrito

O cliente HTTP do servidor inicia a conexão de dentro da VPC privada sem validar o IP resolvido ou sub-rede.

Step 4

Exfiltração de Identidade e Credenciais em Nuvem

O serviço interno de metadados confia no IP do servidor e retorna credenciais temporárias de IAM ou tokens de acesso.

Código-Fonte: Vulnerável vs. Seguro

✕ IMPLEMENTAÇÃO VULNERÁVEL
<?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 SEGURO E ROBUSTO
<?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;
?>

Lista de Verificação de Segurança para Engenharia

References