不安全的HTTP头注入漏洞与防护(Request Header Injection)
题目描述:
HTTP头注入(Request Header Injection)是一种攻击技术,攻击者通过应用层的输入点,将恶意的换行符(CRLF)注入到HTTP请求头中,从而能够添加、修改或伪造新的HTTP请求头,甚至分割请求、注入额外的HTTP请求(即HTTP响应拆分/请求走私的变种)。此漏洞通常发生在应用程序将用户可控的数据(如URL参数、表单字段、Cookie值)未经适当净化就直接插入到HTTP请求头(如Host、Referer、User-Agent等)的场景中。成功利用可导致缓存投毒、会话固定、开放重定向、绕过安全限制(如CORS、CSRF防护)乃至请求走私等严重后果。
我将分步骤详细讲解其原理、攻击场景、利用方式及防护措施。
第一步:理解HTTP协议头结构与CRLF分隔符
HTTP请求和响应都由“起始行”(请求行/状态行)、头部(Headers)和可选的消息体(Body)组成。头部与起始行之间、每个头部之间、头部与消息体之间,都通过特定的控制字符分隔:
- CR(Carriage Return,回车,ASCII码
0x0D,用\r表示) - LF(Line Feed,换行,ASCII码
0x0A,用\n表示)
标准规定,头部之间用CRLF(即\r\n)分隔,头部与消息体之间用连续两个CRLF(即\r\n\r\n)分隔。
例如一个HTTP请求:
GET /index.php HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: Mozilla\r\n
\r\n
(消息体开始)
如果攻击者能在某个头部的值中注入\r\n,就可以提前结束当前头部,并开始一个新的自定义头部。
第二步:漏洞成因与常见注入点
漏洞根因是:应用程序将用户提供的数据直接拼接进HTTP请求头字符串,且未对数据中的CRLF字符进行过滤或转义。
常见注入点:
- 动态设置Host头:某些应用(如反向代理、负载均衡器)根据用户输入的域名或子域名动态设置Host头。
- Referer头或User-Agent头的生成:从请求参数或会话中读取值并填入这些头。
- 自定义头部的构造:如从URL参数设置
X-Forwarded-For、X-Forwarded-Host等。 - 重定向功能:将用户输入放入
Location头(响应头注入,但原理类似)。
示例漏洞代码(Python伪代码):
user_supplied_host = request.GET.get('domain', 'default.com')
headers = {
'Host': user_supplied_host, # 如果domain参数包含CRLF,则注入发生
'User-Agent': 'MyApp'
}
# 然后headers被用于发送后端HTTP请求
如果攻击者提交:domain=evil.com%0d%0aX-Injected-Header:+hacked(URL编码后%0d%0a为CRLF),实际构造的请求头会变成:
Host: evil.com\r\n
X-Injected-Header: hacked\r\n
User-Agent: MyApp\r\n
从而成功注入了一个自定义头。
第三步:攻击利用场景与危害
攻击者利用CRLF注入可实现多种恶意目的:
-
请求走私(Request Smuggling):注入
\r\n\r\n可提前结束头部,并开始注入第二个请求。例如:
注入evil.com%0d%0aContent-Length:+0%0d%0a%0d%0aGET+/admin+HTTP/1.1%0d%0aHost:+evil.com,可能构造出两个请求,用于绕过前端安全控制。 -
缓存投毒(Cache Poisoning):通过注入
X-Forwarded-Host等头,影响缓存键生成,使缓存存储恶意内容并服务给其他用户。 -
绕过安全机制:
- CORS绕过:注入
Origin: https://trusted.com可绕过服务器的CORS检查。 - CSRF防护绕过:某些框架通过检查自定义头(如
X-Requested-With)防御CSRF,攻击者可注入该头以绕过。 - IP限制绕过:注入
X-Forwarded-For: 127.0.0.1可伪装为本地IP以绕过访问控制。
- CORS绕过:注入
-
会话固定(Session Fixation):注入
Set-Cookie头(在响应头注入场景)可强制用户使用攻击者预设的会话ID。 -
开放重定向:在响应头注入中,向
Location头注入恶意URL可导致重定向到钓鱼网站。
第四步:漏洞检测与利用方法
检测步骤:
- 识别注入点:寻找任何用户输入可能被反射到HTTP头部的功能,如重定向参数、自定义Host、代理功能等。
- 测试CRLF注入:提交包含
%0d%0a(CRLF)和测试字符的输入,如:
param=foo%0d%0aTest-Header:+test - 观察响应:
- 如果服务器返回的响应中出现了
Test-Header: test(在响应头部),说明存在响应头注入。 - 如果应用将输入用于发送后端请求,可通过观察后端日志或利用SSRF技巧(如触发DNS/HTTP请求到攻击者服务器)确认注入成功。
- 如果服务器返回的响应中出现了
- 尝试构造完整攻击:根据目标,尝试注入多个头部或分割请求。
利用工具:Burp Suite的Intruder或Repeater可方便地编码和发送CRLF payload。常用测试payload:
%0d%0aInjected-Header: test%0d%0a%0d%0aGET /admin HTTP/1.1%0d%0aHost: evil.com
第五步:防护与修复措施
多层防御策略如下:
-
输入验证与净化:
- 严格校验用户输入是否符合预期格式(如域名、URL),使用白名单机制。
- 移除或转义所有CRLF字符(
\r、\n及其URL编码形式)以及空格(避免头部续行)。 - 示例代码(Python):
def sanitize_header_value(value): # 移除所有CR、LF字符 value = re.sub(r'[\r\n]', '', value) # 也可禁止控制字符(ASCII < 0x20)除了水平制表符 value = ''.join(char for char in value if ord(char) >= 32 or char == '\t') return value
-
避免直接拼接:
- 使用安全的HTTP库(如Python的
requests、Java的HttpClient)并以其API设置头部,而非手动拼接字符串。 - 确保库会自动处理头部值的格式化。
- 使用安全的HTTP库(如Python的
-
输出编码:
- 如果需要将数据输出到HTTP头,进行适当的编码(如将CRLF转为
%0d%0a文本表示,但通常应直接拒绝)。
- 如果需要将数据输出到HTTP头,进行适当的编码(如将CRLF转为
-
安全配置中间件/服务器:
- 配置反向代理(如Nginx、Apache)拒绝包含非法字符的请求头。
- 使用Web应用防火墙(WAF)规则检测CRLF注入尝试。
-
最小权限原则:
- 限制用户输入影响的范围,如避免用户控制Host头,而是从可信来源获取。
-
代码审查与自动化测试:
- 审查所有将用户输入写入头部的代码。
- 在CI/CD管道中加入安全测试,使用DAST工具扫描CRLF注入漏洞。
第六步:进阶考量与旁路技术
- Unicode与多字节编码绕过:某些解析链可能允许Unicode字符(如
%u000d、%u000a)或UTF-7编码,确保解码后仍进行过滤。 - HTTP/2与HTTP/3:这些协议使用二进制帧,不直接使用CRLF分隔,但若后端降级到HTTP/1.1,仍需防护。
- 链式漏洞利用:结合其他漏洞(如SSRF、开放重定向)扩大影响,例如通过CRLF注入在SSRF中增加恶意头部以绕过内网认证。
总结:HTTP头注入是一种因不当处理用户输入而导致的协议层混淆漏洞。防护核心在于严格验证和净化所有用于构造HTTP头部的用户输入,并遵循安全编码实践。通过深度防御(输入验证、安全库、WAF等)可有效消除此风险。