如何修复服务器端请求伪造?

wen 开源项目 77

如何修复服务器端请求伪造(SSRF)?一文读懂防御与修复策略


📖 目录导读

  1. 什么是SSRF?为什么它如此危险?
  2. SSRF攻击的常见场景与利用方式
  3. 核心修复策略:输入验证与白名单机制
  4. 深度防御:网络层与协议层限制
  5. 代码级修复:URL解析与重定向处理
  6. 实战问答:SSRF修复常见问题与解决方案

什么是SSRF?为什么它如此危险?

服务器端请求伪造(Server-Side Request Forgery, SSRF) 是一种攻击者利用服务器作为代理,向内部或外部系统发起恶意请求的安全漏洞,攻击者通过操控应用程序中的URL参数(?url=http://内部IP),迫使服务器代为请求攻击者指定的目标。

如何修复服务器端请求伪造?

SSRF的核心危害体现在:

  • 内网渗透:绕过防火墙,访问云服务元数据(如AWS http://169.254.169.254/)、数据库、Redis等内部服务。
  • 敏感信息泄露:读取服务器本地文件(如 file:///etc/passwd)或云环境中的密钥。
  • 横向移动:以高权限服务身份发起请求,攻陷其他依赖该服务器的组件。

根据OWASP Top 10 2021,SSRF已被列为独立漏洞类别(A10),其影响力在微服务和云原生架构中持续扩大。


SSRF攻击的常见场景与利用方式

攻击场景 典型表现 危害等级
云元数据攻击 请求 http://169.254.169.254/ 获取临时凭证 ⚠️ 极高
内网端口扫描 168.1.1:3306 发起请求探测MySQL
文件协议读取 file:///etc/shadow 读取系统密码文件 极高
重定向SSRF 攻击者控制域名返回302跳转到内网地址

真实案例:

某电商平台允许用户提交商品图片URL,未加限制直接由服务器下载,攻击者提交 http://内网数据库IP:6379,利用Redis未授权访问写入SSH公钥,最终获取服务器权限。


核心修复策略:输入验证与白名单机制

✅ 方案1:建立严格的白名单(推荐)

# 伪代码示例(Python)
ALLOWED_DOMAINS = ['api.trusted.com', 'cdn.trusted.com', 'images.trusted.com']
def validate_url(user_url):
    parsed = urlparse(user_url)
    if parsed.hostname not in ALLOWED_DOMAINS:
        raise SSRFException("域名不在白名单中")
    return user_url

关键点

  • 白名单应包含协议、域名、端口的组合。
  • 禁止使用 结尾的域名或IP范围模糊匹配。
  • 不建议使用黑名单,因为攻击者总能用新服务绕过(如 254.169.254.xip.io)。

✅ 方案2:IP地址级白名单

如果业务需要动态调用外部API,可将IP段限制为:

0.0.0/8, 172.16.0.0/12, 192.168.0.0/16

但需注意云服务元数据IP(如 254.169.254)也应被拦截。


深度防御:网络层与协议层限制

🔒 网络层:防火墙与出站规则

  • 限制出站流量:通过云平台的网络安全组(Security Group)或本地防火墙,仅允许服务器访问明确需要的IP/端口。
  • 分离网络环境:将业务服务器置于独立VPC,对内部敏感服务仅开放API访问通道。

🔒 协议层:禁用不必要的协议

  • 限制URL协议:只允许 http://https://,禁用 file://, dict://, gopher://, ftp:// 等协议。
  • 配置DNS解析过滤器:有些框架支持对DNS解析结果做二次校验(如 Ruby 的 Resolv 库)。

代码示例(Java):

boolean isValidUrl(String url) {
    try {
        URI uri = new URI(url);
        String scheme = uri.getScheme();
        if (!"http".equals(scheme) && !"https".equals(scheme)) {
            return false;
        }
        InetAddress address = InetAddress.getByName(uri.getHost());
        if (address.isSiteLocalAddress() || address.isLoopbackAddress()) {
            return false;
        }
        return true;
    } catch (URISyntaxException | UnknownHostException e) {
        return false;
    }
}

代码级修复:URL解析与重定向处理

⚙️ 陷阱1:DNS重绑定(DNS Rebinding)

攻击者通过一个域名,使其DNS记录在第一次解析时指向合法IP,后续解析指向内网IP。
修复方法

  1. 单次解析+缓存:先用DNS解析出IP地址,再绑定该IP使用,忽略域名。
  2. 使用连接池锁定IP:Python requests 库可以设置 max_retries 配合自定义解析。

⚙️ 陷阱2:URL标准化绕过

攻击者可能使用:

  • http://内网IP#@白名单域名
  • http://白名单域名:80@内网IP
    修复方法:使用完整的URL解析库(如Python urllib.parse)提取真实主机名,并验证是否等于白名单。

⚙️ 陷阱3:重定向攻击

服务端跟随302跳转到内网地址。
修复方法

session = requests.Session()
session.max_redirects = 0  # 禁止重定向
response = session.get(url, allow_redirects=False)

实战问答:SSRF修复常见问题与解决方案

❓ 问题1:业务需要接收用户提供的URL下载文件,如何安全实现?

回答

  1. 强制要求用户上传文件至官方存储(如S3),再让服务器从可信存储下载。
  2. 若必须接受URL,做三层校验:
    • 白名单域名匹配
    • IP地址非私有、非回环
    • DNS缓存结果校验(防止DNS重绑定)

❓ 问题2:使用第三方库处理URL,如何评估其安全性?

回答

  • 检查库是否提供URL解析后的主机名(host)与原始字符串对比。
  • 确认库是否默认跟随重定向(如 curlCURLOPT_FOLLOWLOCATION 需关闭)。
  • 定期更新库版本,关注CVE报告(guzzlehttp 的SSRF修复补丁)。

❓ 问题3:服务器必须调用内网API(如Redis),如何防止被滥用?

回答

  • 使用只读令牌:让API仅接受内部凭证,且绑定来源IP。
  • 对敏感端口(如6379)做代理封装,只暴露有限的命令集合。
  • 即使是内部调用,也禁止使用URL参数动态指定目标

❓ 问题4:如何检查现有代码是否存在SSRF隐患?

回答

  • 静态扫描:使用工具如 SemgrepCodeQL 查找 urlopencurl_exec 等函数。
  • 动态测试:用 http://169.254.169.254/http://127.0.0.1:22 测试。
  • 注意隐式SSRF:例如Jira插件中的Webhook回调、GitHub Webhook配置等。

SSRF防护的三大原则

  1. 最小权限原则:服务器不应具备访问内网资源的权限,除非业务明确需要。
  2. 白名单优先:永远使用白名单,而不是黑名单,并限制协议类型。
  3. 多层校验:从输入解析、网络层、DNS解析到请求完成,每层都设关卡。

最后提醒:即使在云原生环境中(如Kubernetes),也要禁止Pod获取节点元数据(通过metadata.kubelet限制),并定期进行SSRF专项扫描,安全无小事,一个未被限制的 file:// 协议,足以让整个内网沦陷。

抱歉,评论功能暂时关闭!