如何修复Heroku部署的Rails应用中的Client-side desync漏洞
漏洞描述
Burp扫描显示应用存在Client-side desync漏洞:
服务器似乎易受Client-side desync攻击。向'/get_started'路径发送了一个POST请求,第二个请求作为请求体发送。服务器忽略了Content-Length头且未关闭连接,导致偷渡请求被当作下一个请求。
官方建议修复方向:
你可以通过修补服务器来解决该漏洞,要么正确处理POST请求,要么在处理后关闭连接。你也可以完全禁用连接复用,但这可能会降低性能。你还可以通过启用HTTP/2来解决此问题。
问题场景
我尝试通过添加Rails中间件,给POST请求返回Connection: close头来禁用连接复用,开发环境有效,但Heroku生产环境完全无效:
class CloseConnectionMiddleware def initialize(app) @app = app end def call(env) if env['REQUEST_METHOD'] == 'POST' status, headers, body = @app.call(env) headers['Connection'] = 'close' [status, headers, body] else @app.call(env) end end end Rails.application.config.middleware.insert_before Rack::Sendfile, CloseConnectionMiddleware
原因分析
Heroku架构中存在路由层(Heroku Router),所有请求先经过该层再转发到Rails应用。Router会自主管理连接复用逻辑,即使应用返回Connection: close头,也可能被Router覆盖或忽略,导致应用层设置不生效。
解决方案
方案一:彻底禁用连接复用
通过配置Heroku Router+应用层双重控制,确保请求后关闭连接:
配置Heroku Router禁用长连接
在Heroku CLI执行以下命令,强制Router在每个请求后关闭连接:heroku config:set HEROKU_ROUTER_KEEPALIVE_TIMEOUT=0优化应用层中间件
修改中间件,对所有请求添加Connection: close和Keep-Alive: timeout=0头,确保Router和客户端都收到关闭信号:class CloseConnectionMiddleware def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) headers['Connection'] = 'close' headers['Keep-Alive'] = 'timeout=0' [status, headers, body] end end Rails.application.config.middleware.insert_before Rack::Sendfile, CloseConnectionMiddleware
方案二:启用HTTP/2(推荐)
HTTP/2的帧结构设计从根源避免了请求边界混淆的问题,完全解决Client-side desync漏洞,同时不影响性能:
确保应用使用HTTPS
Heroku默认提供SSL证书,直接使用https://<your-app>.herokuapp.com访问即可,无需额外配置证书。更新Puma版本以支持HTTP/2
在Gemfile中指定最新版Puma:gem 'puma', '~> 6.0'执行
bundle install后重新部署到Heroku。验证HTTP/2状态
使用curl命令确认:curl -I --http2 https://<your-app>.herokuapp.com若返回
HTTP/2 200,则说明HTTP/2已成功启用。
内容的提问来源于stack exchange,提问作者Hakeem Baba

