HTTP 请求走私攻击防御实战:Nginx 反向代理加固配置

适用场景

HTTP 请求走私(HTTP Request Smuggling)攻击主要影响使用反向代理 + 后端应用服务器分层架构的网站:Nginx 与后端 Tomcat、Apache、Java 应用等对请求的解析方式不一致,攻击者构造特殊请求头,让代理与后端解析出不同的请求边界,从而绕过 WAF 检测、窃取其他用户请求、实施缓存投毒或越权访问。本文适用于:使用 Nginx 反代 Java/PHP 应用的网站、已部署 WAF 但怀疑被绕过的场景,以及需要满足等保对 Web 应用防护要求的团队。

前置条件

  • 已部署 Nginx 1.18+ 作为反向代理,且通过 proxy_pass 转发请求
  • 后端为 Tomcat、Jetty、Java 应用等支持 Transfer-Encoding 的服务
  • 具备服务器 root 权限,可修改 Nginx 配置并执行 nginx -t 校验
  • 建议准备一台测试机用于复现走私请求(本文命令均在 Linux 环境验证)

原理说明

HTTP 请求走私的核心是前端代理与后端服务器对请求边界(Content-Length / Transfer-Encoding)解析不一致。两种主流利用方式:

  • CL.TE 走私:前端按 Content-Length 读取整个请求体,后端却优先处理 Transfer-Encoding: chunked,攻击者在 chunked 数据中隐藏第二个请求,后端将其作为独立请求处理,从而绕过前端 WAF 规则。
  • TE.CL 走私:前端按 Transfer-Encoding: chunked 解析,后端却按 Content-Length 解析,攻击者利用长度差异让后端拼接出预期之外的请求。

被走私的”第二请求”可以命中后端未经过 WAF 的 URL,实现管理接口访问、缓存投毒、会话固定甚至窃取用户请求(漏洞可导致 CVE-2019-20372、CVE-2020-10061 等真实漏洞利用)。防御的关键是:统一前后端解析行为,从源头封堵走私通道

操作步骤

步骤一:检测目标是否存在走私风险

先确认 Nginx 配置中是否将原始 Transfer-Encoding 头透传给后端。查看当前配置:

# 检查是否显式处理 Transfer-Encoding
grep -rn "Transfer-Encoding" /etc/nginx/conf.d/ /etc/nginx/nginx.conf
# 用 curl 测试后端是否响应 chunked(若代理把 TE 透传,则存在风险)
curl -sI http://your-backend-server/ -H "Transfer-Encoding: chunked" | grep -i "HTTP/"

步骤二:Nginx 层禁用请求走私通道

在 server 或 location 块中拒绝携带 Transfer-Encoding 的请求,并校验 Content-Length 唯一性(关键参数):

# /etc/nginx/conf.d/smuggling.conf
server {
    listen 443 ssl;
    # 拒绝同时包含 CL 与 TE 头的请求(走私前提)
    if ($request_headers ~* "(Content-Length|Transfer-Encoding)") {
        if ($http_transfer_encoding) {
            return 400;
        }
    }
    # 更推荐:用 map 精确判断(避免 if 嵌套)
}

更稳健的做法是在 http 层统一丢弃 TE 头,让后端永远按 Content-Length 解析:

# /etc/nginx/conf.d/proxy_security.conf(http 层 include)
proxy_set_header Transfer-Encoding "";
# 校验请求头唯一性:双 CL 头直接拒绝
more_clear_headers Transfer-Encoding;
# 若未安装 headers-more 模块,可改用 Lua 或直接在 server 层做限制

步骤三:限制请求方法并收紧大小(推荐)

server {
    # 仅允许业务所需方法,阻断 CONNECT/TRACE 等走私常用通道
    if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) {
        return 405;
    }
    # 限制请求体与请求头大小,压缩走私载荷空间
    client_max_body_size 10m;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 8k;
}

步骤四:后端与 WAF 联动加固

  • 后端 Tomcat:在 server.xml 的 Connector 上配置 maxHttpHeaderSizerejectIllegalHeader(Tomcat 9.0.31+ 支持)
  • WAF 侧:启用 OWASP CRS 规则集中的 920420(请求头大小)、920310/920320(请求走私特征)规则
  • 升级所有组件:Nginx 1.18.0+、Tomcat 9.0.40+ 已修复大部分走私 CVE

配置验证

# 1) 语法校验并重载
nginx -t && nginx -s reload
# 2) 验证携带 TE 头的请求被拒绝(应返回 400)
curl -i http://your-site/ -H "Transfer-Encoding: chunked" --data-binary "0" | head -5
# 3) 验证双 CL 头请求被拒绝
curl -i http://your-site/ -H "Content-Length: 1" -H "Content-Length: 2" -X POST -d "a" | head -5
# 4) 正常请求应返回 200,业务不受影响
curl -s -o /dev/null -w "%{http_code}\n" http://your-site/

常见问题

FAQ 1:拦截 TE 头会影响正常的分块上传吗?

不会。Nginx 默认会自动处理请求体并使用自己的 chunked 解码逻辑,业务侧无需感知。被拒绝的是客户端显式携带的 TE 头,正常浏览器上传不携带该头。若确有客户端强制使用 chunked,可在 Nginx 层将其规范化后再转发(由 Nginx 重编码),而非透传原始头。

FAQ 2:为什么我测试时走私请求返回的是 200?

说明 Nginx 与后端当前解析行为一致(或 Nginx 已自行解码 chunked),走私无法成功。这属于安全状态,但仍建议按本文步骤四加固并定期检查组件版本,因为后端升级可能改变解析行为。

FAQ 3:只配置 Nginx 够吗?

不够。请求走私是前后端共同问题,必须两端统一:代理侧拦截、后端侧拒绝非法头、WAF 侧加规则,三者缺一不可。只在一端加固可能被另一端的新特性绕过。

总结

HTTP 请求走私攻击的根源是前后端解析不一致,防御思路是”统一解析、源头阻断”。通过 Nginx 丢弃 Transfer-Encoding、拒绝双 Content-Length、限制请求方法,再配合后端 Connector 参数与 WAF 规则,可系统性消除走私通道。建议将以上配置纳入服务器安全基线,并在每次组件升级后重新验证。