本文目录导读:

在 PHP 中防御 SSRF(Server-Side Request Forgery,服务端请求伪造)攻击,核心思路是 严格限制服务端发起的网络请求,防止攻击者利用漏洞访问内部网络或恶意外部资源。
以下是 PHP 防御 SSRF 的 7 个核心策略,按推荐程度排序:
白名单机制(最有效)
只允许访问预定义的、受信任的主机或 IP 地址。
<?php
function safeCurl($url) {
$whitelist = [
'api.trusted-service.com',
'www.example.com',
'10.0.0.1' // 仅限特定内部服务
];
$host = parse_url($url, PHP_URL_HOST);
if (!in_array($host, $whitelist)) {
throw new Exception("Host not allowed");
}
// 执行请求...
}
IP 地址验证与过滤
过滤私有 IP 地址(如 10.x.x.x、172.16-31.x.x、192.168.x.x)和保留地址(127.0.0.1、0.0.0.0)。
<?php
function isPrivateIp($ip) {
$privateRanges = [
'10.0.0.0' => '8',
'172.16.0.0' => '12',
'192.168.0.0' => '16',
'127.0.0.0' => '8', // loopback
'169.254.0.0' => '16', // link-local
'0.0.0.0' => '8' // 通常表示所有接口
];
foreach ($privateRanges as $range => $mask) {
if (ipInRange($ip, $range, $mask)) {
return true;
}
}
return false;
}
function safeRequest($url) {
$host = parse_url($url, PHP_URL_HOST);
// 先解析为 IP(防止 DNS 欺骗,多次解析确认)
$ip = gethostbyname($host);
if (isPrivateIp($ip)) {
throw new Exception("Access to internal IP denied");
}
// 额外检查 IPv6 的私有地址(fd00::/8 等)
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$ipv6 = inet_pton($ip);
if (strlen($ipv6) >= 8 && ($ipv6[0] & 0xfe) === 0xfc) {
throw new Exception("Private IPv6 denied");
}
}
// 执行 cURL 请求...
}
禁用重定向跟随
攻击者可能利用 HTTP 重定向将请求引导到内部资源。
<?php $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // 关闭自动跟随重定向 // 或者手动处理重定向并验证每个跳转的目标
更好的做法是拦截重定向并重新验证:
<?php curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_MAXREDIRS, 5); // 限制重定向次数 // 或者使用 CURLOPT_PROTOCOLS 限制协议
使用 parse_url 二次验证
攻击者可能利用 URL 解析差异绕过验证(如 http://evil.com\@internal 或 http://evil.com#@internal)。
<?php
function validateUrl($url) {
$parsed = parse_url($url);
// 检查是否包含用户信息(@ 符号前面部分)
if (isset($parsed['user']) || isset($parsed['pass'])) {
throw new Exception("URL with credentials not allowed");
}
// 检查 scheme
$allowedSchemes = ['http', 'https'];
if (!in_array($parsed['scheme'], $allowedSchemes)) {
throw new Exception("Scheme not allowed");
}
// 检查路径是否包含 ../ 等路径穿越
if (preg_match('/\.\.\//', $parsed['path'])) {
throw new Exception("Path traversal detected");
}
return true;
}
对 cURL 进行精细化配置
<?php
function secureCurlRequest($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_FOLLOWLOCATION => false, // 手动处理重定向
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS, // 限制协议
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
CURLOPT_MAXREDIRS => 0, // 禁止重定向
CURLOPT_SSL_VERIFYPEER => true, // 验证 SSL 证书
CURLOPT_SSL_VERIFYHOST => 2,
// 禁止使用本地文件协议(避免 file:// 攻击)
CURLOPT_FILE => null,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
// 检查最终请求的 URL 是否合法
if ($finalUrl !== $url) {
throw new Exception("URL changed during request");
}
curl_close($ch);
return $response;
}
网络层限制(最底层防御)
在服务器防火墙层面限制出站流量,只允许必要的出站 IP/端口。
# iptables 禁止访问私有 IP 地址 iptables -A OUTPUT -d 10.0.0.0/8 -j DROP iptables -A OUTPUT -d 172.16.0.0/12 -j DROP iptables -A OUTPUT -d 192.168.0.0/16 -j DROP iptables -A OUTPUT -d 127.0.0.0/8 -j DROP iptables -A OUTPUT -d 169.254.0.0/16 -j DROP # 只允许访问特定外部服务 iptables -A OUTPUT -d api.example.com -j ACCEPT iptables -A OUTPUT -j DROP # 默认拒绝所有其他出站
防御 DNS 重绑定攻击
攻击者可能使用域名在第一次解析时返回正常 IP,第二次返回内部 IP,使用 多次 DNS 解析并比较 IP:
<?php
function dnsRebindingCheck($host) {
$ips1 = dns_get_record($host, DNS_A);
$ips2 = dns_get_record($host, DNS_A);
if ($ips1 !== $ips2) {
throw new Exception("DNS rebinding attack detected");
}
// 或者使用 gethostbyname 获取一个 IP,再使用 dns_get_record 获取多个
$ip1 = gethostbyname($host);
$records = dns_get_record($host, DNS_A);
$ip2 = $records[0]['ip'] ?? '';
if ($ip1 !== $ip2) {
throw new Exception("Inconsistent DNS resolution");
}
}
综合防御示例
<?php
class SsrfGuard {
private $whitelist = [];
private $blockPrivate = true;
public function validate($url) {
$parsed = parse_url($url);
// 1. 检查 scheme
if (!in_array($parsed['scheme'], ['http', 'https'])) {
throw new \Exception("Invalid scheme");
}
// 2. 检查白名单
if (!empty($this->whitelist)) {
if (!in_array($parsed['host'], $this->whitelist)) {
throw new \Exception("Host not in whitelist");
}
}
// 3. 阻止私有 IP
if ($this->blockPrivate) {
$ip = gethostbyname($parsed['host']);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
throw new \Exception("Private IP not allowed");
}
}
// 4. 禁止路径穿越
if (preg_match('/\.\.\//', $parsed['path'] ?? '')) {
throw new \Exception("Path traversal detected");
}
return true;
}
public function fetch($url) {
$this->validate($url);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
CURLOPT_SSL_VERIFYPEER => true,
]);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
}
总结防御要点
- 永远不要信任用户输入的 URL,即使是看起来正常的域名。
- 优先使用白名单,如果无法使用白名单,至少使用黑名单过滤私有 IP。
- 禁用 cURL 的重定向 或者手动验证每个跳转。
- 限制协议 只允许 http/https,禁止 file/ftp/gopher 等协议。
- 在系统和网络层面 配置防火墙规则限制出站流量。
- SSRF 漏洞常见于:图片抓取、网页截图、URL 预览、API 代理、文件下载等功能。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播