CRLF注入攻击原理与防御详解
字数 2258 2025-12-08 05:34:42
CRLF注入攻击原理与防御详解
CRLF注入攻击是一种利用HTTP协议中特殊控制字符(回车符CR,ASCII码0x0D;换行符LF,ASCII码0x0A)的漏洞,通过在用户输入中注入这些字符,来操纵HTTP响应或请求的结构,从而实现恶意目的的Web安全攻击。它也称为"HTTP响应拆分"攻击的广义形式。
一、基础知识:HTTP协议中的CRLF
- HTTP协议结构:HTTP请求和响应都是由"头部"和"可选的正文"组成,头部和正文之间通过一个空行(即连续的两个CRLF序列:
\r\n\r\n)分隔。而头部中的每一行(每个头部字段)都以单个CRLF(\r\n)结尾。 - 控制字符的意义:在HTTP标准中,
\r\n(CRLF)是标准的行结束符。因此,如果一个应用程序将用户可控的数据,未经过滤地插入到HTTP响应头或请求头中,攻击者就有可能注入自己的\r\n,从而"分割"出新的行,甚至"分割"出头部与正文的边界。
二、攻击原理详解
攻击的核心在于应用程序对用户输入处理不当,允许CRLF字符被原样输出到HTTP流中。
- 示例:一个易受攻击的响应头设置
假设一个Web应用有一个重定向功能,URL参数target用于设置Location响应头。
如果代码是:HTTP/1.1 302 Found Location: https://example.com/redirect?target=user_profile ...response.setHeader("Location", userInput);,且userInput来自URL参数target,未做过滤。 - 攻击过程:
- 攻击者构造一个恶意的URL:
https://example.com/redirect?target=https://evil.com%0d%0aSet-Cookie:%20sessionid=evil%0d%0a是URL编码后的\r\n。
- 服务器收到请求后,直接将参数值设置到
Location头部。 - 形成的HTTP响应会变成:
HTTP/1.1 302 Found Location: https://evil.com Set-Cookie: sessionid=evil ... - 攻击者成功注入了一个新的HTTP响应头
Set-Cookie。如果攻击者能够注入两个连续的\r\n(%0d%0a%0d%0a),他就可以在空行后开始写入HTTP响应正文,从而实现更彻底的响应拆分,例如注入一个完整的恶意HTML页面或JavaScript代码。
- 攻击者构造一个恶意的URL:
三、攻击类型与场景
- HTTP响应头注入(狭义):
- 如上述例子,在现有响应头字段的值中注入
\r\n,创建新的恶意响应头。常见注入点包括:Location,Set-Cookie,Content-Disposition(影响下载文件名),以及任何其他反射用户输入的头部字段。 - 危害:会话固定(设置恶意Session ID)、跨站脚本(通过注入包含JavaScript的头部,如
X-Header: <script>...</script>,如果页面存在反射这些头的逻辑)、缓存投毒(注入影响缓存的头部如Cache-Control)等。
- 如上述例子,在现有响应头字段的值中注入
- HTTP响应拆分(经典形式):
- 注入两个连续的
\r\n\r\n,提前结束HTTP头部,并开始构造攻击者控制的HTTP响应正文。 - 危害:实现完全的响应替换。例如,攻击者可以构造一个看似来自原站点的响应,包含恶意脚本、钓鱼表单,或欺骗浏览器缓存此恶意响应(针对其他用户)。
- 注入两个连续的
- 日志文件注入(Log Injection):
- 当用户输入(如User-Agent, Referer)被记录到日志文件时,如果日志是文本格式且以行分隔,注入
\r\n可以伪造新的日志条目,干扰日志分析、审计或注入恶意命令(如果日志后续被不安全地处理)。
- 当用户输入(如User-Agent, Referer)被记录到日志文件时,如果日志是文本格式且以行分隔,注入
- HTTP请求注入(较少见):
- 在用户输入被用于构造后端发起的HTTP请求(如在某些代理或SSRF场景中)时,注入
\r\n可以污染请求头,可能用于攻击后端系统。
- 在用户输入被用于构造后端发起的HTTP请求(如在某些代理或SSRF场景中)时,注入
四、防御措施
防御的关键在于对输出到HTTP头(或日志)的任何用户输入进行严格的过滤或编码。
- 输入验证与过滤(首选):
- 严格的白名单验证:对于确定头部值的内容(如重定向URL),应验证其是否符合预期的格式(例如,是否为有效的、允许的站内URL或已知安全的外部URL)。
- 删除或转义CRLF字符:在将用户输入放入HTTP头之前,移除或编码所有可能的换行符变体(
\r,\n,%0d,%0a, URL编码、Unicode编码等)。
- 安全的API使用:
- 使用编程语言或框架提供的、安全的API来设置响应头。这些API通常会内部处理字符验证。例如,避免直接拼接字符串来构造头部。
- 对于重定向,使用框架的重定向函数(如
HttpServletResponse.sendRedirect(url)),它们会进行基本验证,而非手动设置Location头。
- 编码输出:
- 如果用户输入必须出现在HTTP头中且可能包含特殊字符,应根据RFC规范进行适当的编码。但对于大多数头部字段值,避免放入不可信数据是更安全的做法。
- 安全开发意识:
- 在安全编码规范中明确禁止将未经验证的用户输入直接输出到HTTP响应头、请求头或日志行中。
- WAF(Web应用防火墙):
- 部署WAF可以配置规则来检测和阻断包含恶意CRLF序列的请求,作为一道补充防线。
总结:CRLF注入攻击利用的是HTTP协议解析与应用程序输入处理之间的不一致性。其防御并不复杂,核心在于对输出到协议控制结构(如头部)的数据保持警惕,并进行严格的净化处理。将此作为安全编码的基本要求,能有效消除此类漏洞。