代理转发请求过程中有个很常用的头,叫做X-Forward-Host,会让攻击者恶意利用进行缓存投毒。
一.问题背景
在一个典型的Web架构中,用户访问公网域名
www.example.com
,流量首先到达部署在公网的负载均衡器。负载均衡器负责将请求分发到内网多台应用服务器(如192.168.1.20
)中的某一台,以实现高可用和扩展性。核心矛盾点:
虚拟主机依赖: 一台内网应用服务器通常通过虚拟主机技术托管数十个网站,它完全依赖HTTP请求头中的
Host
字段来判断用户想访问哪个具体网站。内外网寻址差异: 公网域名
www.example.com
在内部网络可能无法解析或解析到错误地址,导致负载均衡器的转发请求失败。二、 核心挑战
如果负载均衡器将原始请求(
Host: www.example.com
)直接转发给内网服务器192.168.1.20(app-server-1.internal)
,会导致两个问题:
路由错误: 请求发往
www.example.com
而非目标服务器的内网IP,可能在内网中路由失败。网站识别错误: 应用服务器
192.168.1.20
收到Host: www.example.com
的请求后,会试图寻找自身配置中名为www.example.com
的站点。如果找不到,将返回错误或默认页面,而非用户想要的网站。三、 解决方案
负载均衡器在转发请求时,执行以下两个关键操作:
(1)修改目标地址与Host头(用于正确路由):
将请求的目标地址改为应用服务器的内网IP或内部域名(如
app-server-1.internal
)。目的:确保请求能在内网环境中被准确路由到目标机器,并被其接受。
(2)添加X-Forwarded-Host头(用于保留原始信息):
在修改
Host
头之前,将原始的Host
值(www.example.com
)存入一个名为X-Forwarded-Host
的自定义HTTP头中。目的:无损地传递用户的原始访问意图,供后端应用服务器识别。
转发前后的请求头变化:
用户 -> 负载均衡器的请求头:
负载均衡器 -> 应用服务器 的请求头:
四、 后端应用服务器的配合
应用服务器(如Nginx、Apache)必须被配置为能够识别和处理
X-Forwarded-Host
头。五、 安全风险
上述方案引入了一个关键的安全依赖:后端服务器必须无条件信任由负载均衡器设置的
X-Forwarded-Host
等头。如果配置不当,攻击者可以伪造这些头信息,进行缓存投毒攻击。
攻击过程如下:
第1步:攻击者发送恶意请求
攻击者直接向负载均衡器发送一个精心构造的 HTTP请求。他手动添加了
X-Forwarded-Host
头,试图覆盖其值。攻击者 -> 负载均衡器:
请注意: 攻击者完全可以在自己的客户端(如Burp Suite)中轻松地设置任何HTTP头。
第2步:负载均衡器处理并转发请求
负载均衡器收到此请求,按照既定规则进行处理:
它看到
Host: www.example.com
,将其改写为Host: 192.168.1.20
。它看到请求中已经有一个
X-Forwarded-Host
头。负载均衡器的常见行为是:要么追加一个值,要么直接覆盖它。在很多默认配置下,它可能会信任并保留客户端传来的这个头,或者在其后追加自己的值(如X-Forwarded-Host: evil.com, www.example.com
),这取决于具体配置。最危险的情况就是它直接原样转发。我们假设一个不安全的配置,负载均衡器简单地转发了客户端提供的头:
负载均衡器 -> 后端服务器 (192.168.1.20):
第3步:后端服务器生成恶意响应
后端服务器
192.168.1.20
收到请求。它的应用逻辑运行:
它发现了
HTTP_X_FORWARDED_HOST
存在,其值为evil.com
。它完全信任这个值,并用它来生成页面内容。
后端服务器 -> 负载均衡器: 返回包含了指向攻击者控制域名
evil.com
的脚本。第4步:缓存服务器存储恶意响应(投毒)
负载均衡器/缓存服务器收到后端返回的这个响应。
生成缓存键 :它的缓存键规则是
Host + URL
。它看的是最初收到的请求中的Host
头,即www.example.com
,加上路径/index.html
。所以缓存键是www.example.com/index.html
。决定缓存 :它看到响应头中有
Cache-Control: public, max-age=3600
,于是决定缓存此响应。执行存储 :它将这个恶意响应,存储到了
www.example.com/index.html
这个缓存键下。第5步:无辜用户访问中毒的缓存
此后一小时内,任何普通用户访问
https://www.example.com/index.html
。
他们的请求
GET /index.html HTTP/1.1 Host: www.example.com
到达负载均衡器。负载均衡器生成缓存键
www.example.com/index.html
,立即命中第4步中存储的恶意缓存。负载均衡器不再向后端请求,直接将该恶意响应返回给用户。
用户的浏览器解析HTML,向
evil.com
请求并执行那个恶意的app.js
脚本。攻击者成功窃取用户cookie或其他敏感信息。解决办法:
防御策略1:严格校验与过滤HTTP头
漏洞代码示例(javascript):
防御策略2:谨慎设置缓存响应头
对包含用户动态内容的页面,正确设置
Cache-Control
头,避免其被公共缓存。防御策略3:配置缓存键(Cache Key)
默认的缓存键可能只包含
req.url
和req.http.Host
。我们需要修改它,将可能影响响应内容的头(如X-Forwarded-Host
)加入缓存键。这样配置后,一个带有X-Forwarded-Host: evil.com
的请求和正常的请求,会因为缓存键不同而被区别对待,无法污染公共缓存。