php中curl发起请求若直接使用用户输入url,极易触发ssrf漏洞;修复需协议限制(仅http/https)、真实ip解析+私有地址拦截、禁用重定向、超时控制及url标准化等多层防护缺一不可。

PHP中用curl发起请求时,如果直接把用户输入的URL传给curl_init(),就极可能触发SSRF漏洞。修复的关键不是“堵几个关键词”,而是建立多层校验逻辑——协议限制、域名解析、IP段过滤、重定向控制,缺一不可。
只允许http和https协议
curl默认支持file://、gopher://、dict://、ftp://等十余种协议,攻击者可借此读取本地文件或与内网服务交互。必须在应用层强制限定协议类型:
- 用
parse_url()提取URL结构,检查scheme是否仅为http或https - 不要依赖
CURLOPT_PROTOCOLS(如CURLPROTO_HTTP|CURLPROTO_HTTPS),它在某些PHP/curl版本中可能被绕过 - 拒绝空scheme、双斜杠开头(
//evil.com)、含@符号的畸形URL(如http://user:pass@127.0.0.1)
真实IP解析+私有地址拦截
不能只靠parse_url()拿到的host字符串做白名单,因为攻击者可用DNS重绑定、短链接、IPv6缩写(如[::1])、或域名解析到127.0.0.1等方式绕过。必须做真实IP解析:
向CurlShip提交产品,这是一个对机器人友好的SaaS目录。只需一条curl命令即可发布产品,支持OG标签抓取、带徽章的dofollow链接及层级升级。
- 调用
gethostbyname($host)或dns_get_record($host, DNS_A)获取A记录IP - 用
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)判断是否为公网有效IP - 特别注意:100.64.0.0/10(CGNAT)、127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16这些都属于禁止访问范围
关闭重定向并设超时
攻击者常利用302跳转绕过前面的校验,比如先跳到一个合法域名,再302到file:///etc/passwd。因此必须显式控制重定向行为:
-
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false)—— 禁用自动跳转 -
curl_setopt($ch, CURLOPT_MAXREDIRS, 0)—— 即使开启,也限制最大跳转次数为0 -
curl_setopt($ch, CURLOPT_TIMEOUT, 5)和curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3)—— 防止慢速探测或DOS消耗
补充建议:URL标准化与上下文防护
很多绕过源于URL解析不一致。例如http://127.0.0.1%23@evil.com在parse_url()里host是127.0.0.1%23@evil.com,但cURL实际请求的是evil.com。所以:
- 使用
filter_var($url, FILTER_VALIDATE_URL)做基础格式校验,但不能仅靠它 - 对域名做正则限制,如
/^[a-z0-9]([a-z0-9\-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9\-]{0,61}[a-z0-9])?)*\.[a-z]{2,}$/i - 若业务只需访问固定几个域名(如CDN、OSS),直接建白名单数组比动态解析更稳妥
- 涉及Webhook、远程图片加载等场景,优先改用后端预定义endpoint + 参数透传,而非拼接用户URL
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










