游客发表

ssrf漏洞解决办法

发帖时间:2026-10-17 05:02:00

```html ssrf漏洞解决办法

上周帮一个朋友看他的漏洞侧写项目,有个功能是解决传个图片链接,后端去抓取。办法我随手填了 http://127.0.0.1:6379,漏洞页面卡了三秒,解决然后返回一个 Redis 的办法 NOAUTH 错误。他脸色一下就变了。漏洞这就是解决 SSRF 最典型的场景——你的服务器成了攻击者的代理,帮他去摸内网里那些本不该暴露的办法东西。

SSRF 的漏洞根子在于“信任了用户提供的 URL”。不管你是解决用 file_get_contents、requests.get 还是办法 curl_exec,只要目标地址来自前端,漏洞就等于把内网探测的解决钥匙交出去了。很多人第一反应是办法加个黑名单,把 127.0.0.1、169.254.169.254 这些 IP 封掉。但黑名单永远漏风:0177.0.0.1、2130706433、[::1]、localtest.me 指向 127.0.0.1 的域名……绕过方式比你想象的要多得多。

我在实际代码审计里见过最离谱的修复,是把 localhost 字符串替换成空。结果攻击者用 127.0.0.1 照样打穿。黑名单思路本身就不适合做安全边界,因为解析器和网络栈的行为太复杂了,你永远不知道下一个解析差异在哪。

那怎么办?核心原则就一条:不要自己解析和校验 URL,而是让发起请求的组件自己去解析,你只做白名单或协议限制。 听起来有点绕,我举个例子。PHP 里很多人会 parse_url 拿到 host,然后 gethostbyname 解析成 IP,再判断 IP 是不是内网。但这里有个经典坑:parse_url 和 curl 对 URL 的理解可能不一致。比如 http://foo@127.0.0.1/,parse_url 认为 host 是 127.0.0.1,没问题。但 http://127.0.0.1#@evil.com/ 呢?不同语言不同库解析结果天差地别。你校验的 host 和实际请求的 host 不是同一个,校验就白做了。

更稳的做法是彻底禁止用户控制协议和主机。比如图片抓取,只允许 http 和 https,并且域名必须在你预设的白名单里。白名单可以是业务上真的需要抓取的那些图床域名。如果业务要求必须支持任意 URL,那就得走网络层隔离。

网络层隔离是我最推荐的方式。把发起请求的服务部署在一个独立的 VPC 或者容器里,这个环境没有任何内网路由,只能访问公网。就算攻击者让你请求 http://169.254.169.254/latest/meta-data/,请求也发不出去,因为那个网段根本不可达。云上可以用安全组或者网络 ACL 来限制出站流量,只放行 80 和 443 到公网。这比在代码里写一堆正则要可靠得多。

还有一种情况是必须请求内网服务,比如你有个内部 API 网关。这时候可以用“代理 + 鉴权”的模式:用户传的 URL 不直接请求,而是发给一个专门的转发服务,这个服务只认签名后的请求,并且请求的目标地址是预先注册过的。相当于把 SSRF 的风险转移到了一个受控组件上。

另外,别忘了重定向。你校验了初始 URL 是安全的,但对方返回 302 跳到 http://127.0.0.1,很多 HTTP 客户端默认会跟着跳。所以必须禁用自动重定向,或者每跳一次都重新做一遍校验。Python 的 requests 里 allow_redirects=False 要显式加上,然后自己处理 Location 头。

DNS 重绑定也值得提一句。攻击者控制一个域名,第一次解析返回公网 IP 让你通过校验,第二次解析返回 127.0.0.1 让请求打到内网。防御方法是解析一次后把 IP 固定住,后续请求直接用 IP 加 Host 头,不再走 DNS。但这样又可能破坏 SNI 和虚拟主机,得看具体场景权衡。

最后说个真实案例。某次渗透测试里,目标有个导出 PDF 的功能,传 URL 生成快照。我试了 http://127.0.0.1:8080/actuator/env,直接返回了环境变量,里面有数据库密码。他们代码里其实做了黑名单,但只过滤了 127.0.0.1 字符串,没过滤 0x7f000001。修复方案后来改成了用 curl 的 CURLOPT_PROTOCOLS 限制协议,并且把服务移到独立子网,出站只允许到公网 CDN。代码改动不到十行,但彻底堵死了。

所以别指望一个函数解决 SSRF。它是个架构问题,不是代码片段问题。能白名单就白名单,不能白名单就网络隔离,实在不行再叠加协议限制和重定向禁用。顺序别搞反了。

```

    热门排行

    友情链接