Abstract
HTTP 响应头文件中包含未验证的数据会引发 cache-poisoning、 cross-site scripting、 cross-user
defacement、 page hijacking、 cookie manipulation 或 open redirect
Explanation
以下情况中会出现 Header Manipulation 漏洞: 1. 数据通过一个不可信赖的数据源进入 Web 应用程序,最常
见的是 HTTP 请求。 2. 数据包含在一个 HTTP 响应头文件里,未经验证就发送给了 Web 用户。 如同许多软
件安全漏洞一样, Header Manipulation 只是通向终端的一个途径,它本身并不是终端。从本质上看,这些漏
洞是显而易见的:一个攻击者将恶意数据传送到易受攻击的应用程序,且该应用程序将数据包含在 HTTP 响
应头文件中。 其中最常见的一种 Header Manipulation 攻击是 HTTP Response Splitting。为了成功地实施
HTTP Response Splitting 盗取,应用程序必须允许将那些包含 CR(回车,由 %0d 或 \r 指定)和 LF(换
行, 由 %0a 或 \n 指定)的字符输入到头文件中。攻击者利用这些字符不仅可以控制应用程序要发送的响应
剩余头文件和正文,还可以创建完全受其控制的其他响应。 如今的许多现代应用程序服务器可以防止 HTTP
头文件感染恶意字符。例如,如果尝试使用被禁用的字符设置头文件,最新版本的 Apache Tomcat 会抛出
IllegalArgumentException。如果您的应用程序服务器能够防止设置带有换行符的头文件,则其具备对
HTTP Response Splitting 的防御能力。然而,单纯地过滤换行符可能无法保证应用程序不受 Cookie
Manipulation 或 Open Redirects 的攻击,因此必须在设置带有用户输入的 HTTP 头文件时采取措施。 示例:
下列代码片段会从 HTTP 请求中读取网络日志项的作者名字 author,并将其置于一个 HTTP 响应的 cookie
头文件中。
String author = request.getParameter(AUTHOR_PARAM);
…
Cookie cookie = new Cookie(“author”, author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);
假设在请求中提交了一个字符串,该字符串由标准的字母数字字符组成,如“Jane Smith”,那么包含该
Cookie 的 HTTP 响应可能表现为以下形式:
HTTP/1.1 200 OK
…
Set-Cookie: author=Jane Smith
…
然而,因为 cookie 值来源于未经校验的用户输入,所以仅当提交给 AUTHOR_PARAM 的值不包含任何 CR 和
LF 字符时,响应才会保留这种形式。如果攻击者提交的是一个恶意字符串,比如“Wiley Hacker\r\nHTTP/1.1
200 OK\r\n…”,那么 HTTP 响应就会被分割成以下形式的两个响应:
HTTP/1.1 200 OK
…
Set-Cookie: author=Wiley Hacker
HTTP/1.1 200 OK
…
显然,第二个响应已完全由攻击者控制,攻击者可以用任何所需标头和正文内容构建该响应。攻击者可以构
建任意 HTTP 响应,从而发起多种形式的攻击,包括: cross-user defacement、 web and browser cache
poisoning、 cross-site scripting 和 page hijacking。 Cross-User Defacement: 攻击者可以向一个易受攻击
的服务器发出一个请求,导致服务器创建两个响应,其中第二个响应可能会被曲解为对其他请求的响应,而
这一请求很可能是与服务器共享相同 TCP 连接的另一用户发出的。这种攻击可以通过以下方式实现:攻击者
诱骗用户,让他们自己提交恶意请求;或在远程情况下,攻击者与用户共享同一个连接到服务器(如共享代
理服务器)的 TCP 连接。最理想的情况是,攻击者通过这种方式使用户相信自己的应用程序已经遭受了黑客
攻击,进而对应用程序的安全性失去信心。最糟糕的情况是,攻击者可能提供经特殊技术处理的内容,这些
内容旨在模仿应用程序的执行方式,但会重定向用户的私人信息(如帐号和密码),将这些信息发送给攻击
者。 缓存中毒: 如果多用户 Web 缓存或者单用户浏览器缓存将恶意构建的响应缓存起来,该响应的破坏力
会更大。如果响应缓存在共享的 Web 缓存(如在代理服务器中常见的缓存)中,那么使用该缓存的所有用户
都会不断收到恶意内容,直到清除该缓存项为止。同样,如果响应缓存在单个用户的浏览器中,那么在清除
该缓存项以前,该用户会不断收到恶意内容。然而,影响仅局限于本地浏览器的用户。 Cross-Site
Scripting: 一旦攻击者控制了应用程序传送的响应,就可以选择多种恶意内容并将其传播给用户。 CrossSite Scripting 是最常见的攻击形式,这种攻击在响应中包含了恶意的 JavaScript 或其他代码,并在用户的浏
览器中执行。基于 XSS 的攻击手段花样百出,几乎是无穷无尽的,但通常它们都会包含传输给攻击者的私有
数据(如 Cookie 或者其他会话信息)。在攻击者的控制下,指引受害者进入恶意的网络内容;或者利用易受
攻击的站点,对用户的机器进行其他恶意操作。对于易受攻击的应用程序用户,最常见且最危险的攻击就是
使用 JavaScript 将会话和身份验证信息返回给攻击者,而后攻击者就可以完全控制受害者的帐号了。 Page
Hijacking: 除了利用一个易受攻击的应用程序向用户传输恶意内容,还可以利用相同的根漏洞,将服务器生
成的供用户使用的敏感内容重定向,转而供攻击者使用。攻击者通过提交一个会导致两个响应的请求,即服
务器做出的预期响应和攻击者创建的响应,致使某个中间节点(如共享的代理服务器)误导服务器所生成的
响应,将本来应传送给用户的响应错误地传给攻击者。因为攻击者创建的请求产生了两个响应,第一个被解析为针对攻击者请求做出的响应,第二个则被忽略。当用户通过同一 TCP 连接发出合法请求时,攻击者的请
求已经在此处等候,并被解析为针对受害者这一请求的响应。这时,攻击者将第二个请求发送给服务器,代
理服务器利用针对受害者(用户)的、由该服务器产生的这一请求对服务器做出响应,因此,针对受害者的
这一响应中会包含所有头文件或正文中的敏感信息。 Cookie Manipulation: 当与类似跨站请求伪造的攻击
相结合时,攻击者就可以篡改、添加、甚至覆盖合法用户的 cookie。 打开重定向: 如果允许未验证的输入来
控制重定向机制所使用的 URL,可能会有利于攻击者发动钓鱼攻击。
Recommendation
防止 Header Manipulation 漏洞的解决方法是,确保在适当位置进行输入验证并检验其属性是否正确。 由于
Header Manipulation 漏洞出现在应用程序的输出中包含恶意数据时,因此,合乎逻辑的做法是在应用程序输
出数据前一刻对其进行验证。然而,由于 Web 应用程序常常会包含复杂而难以理解的代码,用以生成动态响
应, 因此,这一方法容易产生遗漏错误(遗漏验证)。降低这一风险的有效途径是对 Header Manipulation 也
执行输入验证。 由于 Web 应用程序必须验证输入信息以避免出现其他漏洞(如 SQL Injection),因此,一
种相对简单的解决方法是增强应用程序现有的输入验证机制,增加针对 Header Manipulation 的检查。尽管具
有一定的价值,但 Header Manipulation 输入验证并不能取代严格的输出验证。应用程序可能通过共享的数据
存储器或其他可信赖的数据源接受输入,而该数据存储器所接受的输入源可能并未执行适当的输入验证。因
此, 应用程序不能间接地依赖于该数据或其他任意数据的安全性。这就意味着,避免 Header Manipulation 漏
洞的最佳方法是验证所有应用程序输入数据或向用户输出的数据。 针对 Header Manipulation 漏洞进行验证
最安全的方式是创建一份安全字符允许列表,允许其中的字符出现在 HTTP 响应标头中,并且只接受完全由
这些经认可的字符组成的输入。例如,有效的用户名可能仅包含字母数字字符,帐号可能仅包含 0-9 的数
字。 更灵活的方法是执行拒绝列表,但其安全性较差,该列表会在使用输入之前有选择地拒绝或避免潜在的
危险字符。为了创建这样的列表,首先需要了解在 HTTP 响应头文件中具有特殊含义的一组字符。尽管 CR
和 LF 字符是 HTTP Response Splitting 攻击的核心,但其他字符,如“:”(冒号)和 ‘=’(等号),在响应标头
中同样具有特殊的含义。 在应用程序中确定针对 Header Manipulation 攻击执行验证的正确要点,以及验证
过程中要考虑的特殊字符之后,下一个难点就是确定验证过程中处理各种特殊字符的方式。应用程序应拒绝
任何要添加到 HTTP 响应头文件中的包含特殊字符的输入,这些特殊字符(特别是 CR 和 LF)是无效字符。
许多应用程序服务器都试图避免应用程序出现 HTTP Response Splitting 漏洞,其做法是为负责设置 HTTP
标头和 Cookie 的函数提供各种实现方式,以检验是否存在进行 HTTP Response Splitting 攻击必需的字符。
不要依赖运行应用程序的服务器,以此确保该应用程序的安全。对于任何已开发的应用程序,并不能保证在
其生命周期中它会在哪些应用程序服务器中运行。由于标准和已知盗取方式的演变,我们不能保证应用程序
服务器将继续保持同步。
示例1:
未处理前产生的问题
解决后如下