CRLF注入攻击直接瞄准网站响应头,攻击者通过向输入字段注入回车符(%0D)和换行符(%0A),能够篡改HTTP响应头,实现会话固定、跨站脚本(XSS)甚至重定向用户至恶意网站。防护的核心在于对输出到HTTP响应头中的所有用户输入进行严格的清洗和编码。

理解CRLF注入的攻击原理与危害

HTTP协议依靠回车换行符(CRLF)来分隔头部字段和正文。正常响应头如“Location: /home”后跟一个CRLF。若应用将用户输入未经处理直接放入响应头,攻击者提交“example.com%0D%0ASet-Cookie: malicious=value”,服务器会将其解析为两个头部:Location跳转和一个新的Set-Cookie。这可能导致会话劫持(Session Fixation),攻击者预设会话ID,诱导用户使用从而窃取会话。

更危险的利用是注入整个HTTP响应。例如,在可控制头部内容的场景,输入“%0D%0A%0D%0A<script>alert('XSS')</script>”会在头部后插入空行并开始正文,触发反射型XSS。此外,通过注入“Location: http://恶意网站”可实现钓鱼重定向。这类漏洞常出现在URL重定向、文件下载头部设置、自定义错误页面等环节。

实施响应头清洗的关键防御策略

首要防御是“黑名单过滤”的彻底摒弃。单纯删除“\r\n”字符不足够,因编码绕过(如Unicode、多重编码)层出不穷。应采用“白名单验证”与“编码输出”结合的策略。对所有动态设置响应头的参数,定义严格允许的字符集(如字母、数字、有限符号),拒绝其他字符。

强制实施输出编码。在将数据写入响应头前,必须进行适当的编码。例如,将换行符、回车符转换为无害字符,或使用框架提供的安全函数。在Java中,可使用ESAPI编码器:

ESAPI.encoder().encodeForHTMLAttribute(userInput);

在.NET中,应对头部值进行URL编码或自定义过滤。

设置安全头部增强防护。即使存在注入风险,部署Content Security Policy (CSP) 可限制脚本执行,降低XSS影响。头部如“X-Content-Type-Options: nosniff”防止MIME类型混淆攻击。

具体清洗与验证的代码实现示例

以Python Flask应用为例,实现一个安全的头部设置函数。首先,定义清洗函数移除或替换危险字符:

def sanitize_header_value(value):
    if not value:
        return ""
    # 替换CRLF字符为空格
    value = value.replace('\r', ' ').replace('\n', ' ')
    # 移除其他控制字符(ASCII 0-31, 127)
    value = ''.join(char for char in value if ord(char) >= 32 and ord(char) != 127)
    # 可选:限制长度防止溢出
    return value[:2000]

在设置响应头时调用:

from flask import Response
resp = Response()
safe_value = sanitize_header_value(user_input)
resp.headers['X-Custom-Header'] = safe_value

对于重定向URL,必须验证域名白名单。例如:

allowed_domains = ['example.com', 'trusted.org']
def safe_redirect(url):
    from urllib.parse import urlparse
    parsed = urlparse(url)
    if parsed.netloc and parsed.netloc not in allowed_domains:
        return '/default-safe-page'
    return url

此方法杜绝了开放重定向漏洞,该漏洞常与CRLF注入共生。

服务器配置与框架级防护措施

现代Web框架内置了部分防护,但需正确配置。在Node.js Express中,避免直接使用"res.set()"传入用户输入。使用中间件自动过滤头部值。例如,可编写全局中间件:

app.use((req, res, next) => {
    const originalSetHeader = res.setHeader;
    res.setHeader = function(name, value) {
        if (typeof value === 'string') {
            value = value.replace(/[\r\n]/g, '');
        }
        originalSetHeader.call(this, name, value);
    };
    next();
});

服务器层面,Nginx或Apache应限制自定义头部的长度和内容。在Nginx配置中,可设置:

server {
    # 防止头部注入,限制特殊字符
    location / {
        # 使用$http变量时注意过滤
        proxy_set_header Custom-Header "$http_user_header";
        # 最好使用固定值或严格控制的变量
    }
}

同时,定期更新服务器和框架以修补已知漏洞。

自动化检测与持续监控流程

防护需结合主动检测。使用静态应用安全测试(SAST)工具扫描源代码,查找直接拼接响应头的模式。动态应用安全测试(DAST)工具,如渗透测试,模拟注入攻击。手动测试可借助Burp Suite,在输入中插入%0D%0A观察响应变化。

监控日志中的异常头部活动。例如,日志中出现包含CRLF字符的请求应触发警报。在Splunk或ELK栈中设置规则:

index=web_logs *%0D%0A* OR *%0D* OR *%0A*

实现实时告警。

最后,将响应头清洗纳入开发安全培训。代码审查中,重点关注"Location"、"Set-Cookie"、"X-Forwarded-For"等头部设置点。建立安全编码规范,要求所有响应头输出必须通过中心化的安全函数处理。