HTTP头注入和HTTP响应拆分是Web应用中两种非常危险的安全漏洞,它们的本质都是攻击者通过在用户输入中插入特殊字符(如CRLF换行符),篡改HTTP响应头的结构,从而实现恶意重定向、缓存投毒、会话劫持甚至XSS攻击。修复这类漏洞的核心方法就是:对所有用户输入进行严格的过滤和验证,禁止输入中出现CR(\r)和LF(\n)字符,同时在服务端设置安全的HTTP响应头,如Content-Security-Policy、X-Content-Type-Options等。下面我会从漏洞原理、攻击场景、具体修复方案和最佳实践四个层面,把这件事讲透。
一、HTTP头注入漏洞到底是什么HTTP协议中,响应头和响应体之间是用一个空行(即\r\n\r\n)来分隔的。HTTP头注入漏洞的产生原因是:当Web应用把用户可控的数据直接拼接到HTTP响应头中,而没有对换行符进行过滤时,攻击者就可以通过注入\r\n来伪造新的响应头字段,甚至插入一个完整的恶意响应体。比如一个网站的重定向功能,代码可能是这样的:
Location: http://example.com/redirect?url=用户输入
如果用户输入的是 "http://evil.com%0d%0aSet-Cookie: session=hacked",那么最终的响应头就会变成:
Location: http://example.com/redirect?url=http://evil.com Set-Cookie: session=hacked
这样攻击者就成功注入了一个Set-Cookie头,可以覆盖用户的会话Cookie。这种漏洞在PHP、Java、Python、Node.js等各种后端语言中都可能出现,关键在于开发者是否对用户输入做了安全处理。
二、HTTP响应拆分攻击的原理和危害HTTP响应拆分(HTTP Response Splitting)是头注入的一种进阶利用方式。攻击者不仅注入响应头,还能在响应中插入第二个完整的HTTP响应。这意味着攻击者可以在同一个TCP连接中返回两个响应,第一个是正常的,第二个是恶意的。这种攻击在缓存服务器、代理服务器场景下危害极大,因为恶意响应可能被缓存下来,之后所有访问该URL的用户都会收到被投毒的内容。
具体来说,攻击者构造的输入可能包含两组\r\n\r\n,第一组结束正常响应,第二组开始一个全新的恶意响应。比如在一个使用了代理缓存的网站上,攻击者可以让缓存服务器存储一个包含恶意JavaScript的页面,后续用户访问时直接执行这段脚本,造成大规模XSS攻击。
三、漏洞产生的常见代码场景以下是几种典型的漏洞代码模式,开发者需要重点排查:
场景一:直接拼接重定向URL。PHP代码示例:
<?php
$redirect_url = $_GET['url'];
header("Location: " . $redirect_url);
?>
场景二:设置自定义响应头时使用用户输入。Java代码示例:
String userAgent = request.getHeader("User-Agent");
response.setHeader("X-Custom-Header", userAgent);
场景三:Node.js中拼接响应头:
const url = req.query.redirect;
res.setHeader('Location', url);
res.end();
以上三种写法都没有对输入中的\r和\n字符进行过滤,都存在头注入风险。
四、具体修复方案:输入过滤与输出编码修复HTTP头注入和响应拆分漏洞,需要从输入验证、输出编码、安全头设置三个维度同时入手。
第一步:严格过滤用户输入。所有可能被放入HTTP头的用户数据,都必须移除或拒绝包含CR(\r,ASCII 13)和LF(\n,ASCII 10)的输入。以下是各语言的实现方式:
PHP修复代码:
<?php
function sanitize_header_input($input) {
// 移除所有CR和LF字符
$sanitized = str_replace(array("\r", "\n"), '', $input);
// 如果输入被修改过,说明包含危险字符,可以选择拒绝或记录日志
if ($sanitized !== $input) {
error_log("Potential header injection attempt: " . $input);
return false;
}
return $sanitized;
}
$redirect_url = sanitize_header_input($_GET['url']);
if ($redirect_url !== false) {
header("Location: " . $redirect_url);
} else {
header("Location: /error");
}
?>
Python修复代码:
import re
def sanitize_header_input(input_str):
if re.search(r'[\r\n]', input_str):
raise ValueError("Invalid input: contains CRLF characters")
return input_str
# 使用示例
try:
url = sanitize_header_input(request.args.get('url', ''))
response.headers['Location'] = url
except ValueError:
response.headers['Location'] = '/error'
Java修复代码:
public static String sanitizeHeaderInput(String input) {
if (input == null) return null;
if (input.contains("\r") || input.contains("\n")) {
throw new IllegalArgumentException("Invalid input contains CRLF");
}
return input;
}
第二步:使用白名单验证。与其过滤危险字符,更安全的做法是只允许符合预期格式的输入。比如重定向URL,应该只允许http或https开头的合法URL格式:
<?php
function validate_redirect_url($url) {
if (!preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\/.*)?$/', $url)) {
return false;
}
return $url;
}
?>
第三步:对输出进行URL编码。即使输入通过了验证,在放入HTTP头之前也建议进行URL编码,作为防御纵深:
<?php
$safe_url = urlencode($redirect_url);
header("Location: " . $safe_url);
?>
五、设置安全HTTP响应头作为纵深防御
除了修复代码层面的漏洞,还需要在Web服务器和应用层面配置安全的HTTP响应头,即使头注入被利用,也能降低危害:
Content-Security-Policy(CSP):限制页面可以加载和执行的资源,即使攻击者注入了恶意脚本,CSP也能阻止其执行。
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-random123';
X-Content-Type-Options: nosniff:防止浏览器MIME类型嗅探,阻止攻击者通过注入恶意内容类型来执行脚本。
X-Content-Type-Options: nosniff
X-Frame-Options: DENY:防止页面被嵌入iframe,抵御点击劫持攻击。
X-Frame-Options: DENY
Strict-Transport-Security(HSTS):强制HTTPS连接,防止中间人攻击中的头注入利用。
Strict-Transport-Security: max-age=31536000; includeSubDomains
在Nginx中配置这些头的示例:
server {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
}
六、Web服务器和框架层面的防护配置
很多现代Web框架已经内置了对HTTP头注入的防护机制,但需要正确配置才能生效。比如在Apache中,可以使用mod_headers模块来限制响应头的注入;在Tomcat中,可以配置strict-http-header-validation来自动拒绝包含非法字符的请求。
Nginx层面可以通过限制请求头大小和内容来降低风险:
server {
large_client_header_buffers 4 8k;
# 限制单个请求头的最大长度
client_header_buffer_size 1k;
}
同时建议在WAF(Web应用防火墙)层面配置规则,拦截包含\r\n序列的请求参数,这是一道非常有效的外部防线。
七、测试验证与持续监控修复完成后,必须进行安全测试来验证漏洞是否真正消除。可以使用以下方法:
手动测试:在所有接受用户输入并用于HTTP头的参数中,尝试注入%0d%0a、\r\n、%0a等变体,观察响应是否被正确拒绝或过滤。
自动化扫描:使用OWASP ZAP、Burp Suite等安全工具的主动扫描功能,检测HTTP头注入漏洞。这些工具会自动尝试各种编码变体,比手动测试更全面。
代码审计:定期对涉及HTTP头操作的代码进行安全审计,特别是在代码更新和功能迭代时,新增的重定向、自定义头设置等功能都需要重点审查。
日志监控:在生产环境中开启详细的访问日志记录,监控是否有包含异常字符的请求被频繁提交,这可能是攻击者在进行探测。
八、总结与核心建议HTTP头注入和响应拆分漏洞虽然原理不复杂,但危害范围广、利用门槛低,是Web安全中必须优先修复的高危问题。核心修复思路就三点:第一,永远不要信任用户输入,所有放入HTTP头的数据必须经过严格过滤和白名单验证;第二,使用URL编码等输出编码技术增加防御层次;第三,配置完善的安全HTTP响应头作为纵深防御。把这三层做到位,基本就能杜绝此类漏洞的利用。安全不是一次性的工作,而是需要在开发流程、代码审查、部署配置、持续监控各个环节都保持警惕。
