SSRF 是什么
SSRF(服务端请求伪造)指应用按用户可控的地址发起网络请求,攻击者借此让服务器替他访问本不该触达的位置:内网服务、云平台元数据接口、或是仅监听本机的管理端口。它常出现在「帮用户获取远程内容」的功能里,如抓图、导入链接、生成预览与回调通知,这类功能天然需要出站访问,一旦地址未受约束,服务器就成了攻击者的代理。防护以目标白名单为主,配合地址解析后的校验与出站网络限制。
基本信息
| 风险类别 | OWASP Top 10 2021 A10;CWE-918 |
|---|---|
| 常见入口 | URL 抓取、图片处理、Webhook 回调、PDF 生成、远程导入 |
| 危害 | 内网探测、云元数据读取、内部服务调用、绕过网络访问控制 |
| 防护 | 目标白名单 + 解析 IP 校验 + 禁止跟随重定向 + 出站网络限制 |
| 加固层次 | 应用层校验为主,网络层出站策略兜底 |
| 验证要点 | 校验发生在解析前还是解析后,以及是否固定使用校验过的地址 |
典型场景
- 图片与文件抓取:按用户填写的图片地址下载并转存
- 网页预览与截图:服务端渲染用户提供的链接
- Webhook 与回调配置:用户填写回调地址,服务端主动发起请求
- 文档与报表生成:把远程地址内嵌进 PDF 或导出文件
- 数据导入与同步:从用户提供的地址拉取 CSV 或接口数据
- 第三方集成:按配置的接口地址请求,配置项对普通用户可改
校验要点(Python 示意)
校验要点(Python 示意)
import ipaddress, socket
from urllib.parse import urlparse
def is_allowed(url, allow_hosts):
u = urlparse(url)
if u.scheme not in ('http', 'https'):
return False
if u.hostname not in allow_hosts:
return False
ip = ipaddress.ip_address(socket.gethostbyname(u.hostname))
return not (ip.is_private or ip.is_loopback or ip.is_link_local)
# 校验通过后应使用解析出的 IP 发起连接,避免解析与使用之间被替换
# 同时关闭自动重定向跟随,或对每一跳重新校验绕过手法与对应防护
| 常见绕过 | 说明 | 防护要点 |
|---|---|---|
| 十进制与八进制写法 | 把 IP 换成等值数字形式 | 统一解析为 IP 对象后再判断 |
| IPv6 映射地址 | 用映射到私网的 IPv6 形式绕过字符串匹配 | 覆盖 IPv6 与映射地址判断 |
| 302 跳转 | 首跳合法,跳转到内网地址 | 禁止跟随重定向或逐跳校验 |
| DNS 重绑定 | 校验时解析到公网,使用时解析到内网 | 固定使用校验过的 IP 连接 |
| 云元数据地址 | 访问平台内部元数据接口获取凭据 | 出站策略拦截元数据网段 |
先解析再校验、校验后固定使用该 IP 连接,可缓解解析与使用之间的重绑定问题;出站网络层再限制可访问网段与端口,形成第二道约束。
如果业务只需要访问固定的几个外部合作方,白名单方案成本最低也最稳;只有面向任意地址的开放场景,才需要复杂的地址判定逻辑。