Back to Blog
high SEVERITY8 min read

How Cache-Control Header Injection Happens in Node.js HTTP Libraries and How to Fix It

CVE-2026-13697 is a high-severity vulnerability in the undici HTTP client library where the cache interceptor mishandles malformed Cache-Control directives, potentially leading to information disclosure and denial of service attacks. Upgrading from undici 7.28.0 to 7.29.0 (or 8.9.0 for v8 users) patches this vulnerability by implementing stricter validation of Cache-Control headers. This fix is critical for any Node.js application that relies on undici for HTTP requests, especially those handlin

O
By Orbis AppSec
Published August 28, 2026Reviewed August 28, 2026

Answer Summary

CVE-2026-13697 is a cache-handling vulnerability in undici (Node.js HTTP client) where the cache interceptor mishandles malformed Cache-Control directives, potentially leaking sensitive information or causing denial of service. The vulnerability exists in undici versions before 7.29.0 and 8.9.0. The fix involves upgrading undici and adding dependency overrides to ensure strict validation of Cache-Control headers, preventing attackers from manipulating caching behavior through specially crafted headers.

Vulnerability at a Glance

cweCWE-345 (Insufficient Verification of Data Authenticity), CWE-444 (Inconsistent Interpretation of HTTP Requests)
fixUpgrade undici to 7.29.0 or 8.9.0 with stricter Cache-Control directive validation
riskInformation disclosure (cached sensitive data exposure), Denial of Service via cache poisoning
languageJavaScript/Node.js
root causeInadequate parsing and validation of Cache-Control header directives in undici's cache interceptor
vulnerabilityCache-Control Header Injection / Information Disclosure via Malformed Directives

How Cache-Control Header Injection Happens in Node.js HTTP Libraries and How to Fix It

Introduction

In the node-app repository, a high-severity vulnerability (CVE-2026-13697) was discovered in the undici HTTP client library's cache interceptor. The vulnerability stems from inadequate parsing and validation of the Cache-Control HTTP header, allowing attackers to craft malformed directives that bypass cache validation logic. This could result in sensitive data being cached inappropriately or cache poisoning attacks that cause denial of service.

The vulnerable code path handles user-influenced HTTP responses, and the cache interceptor processes the Cache-Control header without sufficiently strict validation. When an attacker sends a response with a specially crafted Cache-Control header containing malformed directives, the interceptor's parsing logic fails to properly reject or sanitize these directives, leading to unexpected caching behavior.

This matters for developers because:
- Sensitive data exposure: Cached responses containing authentication tokens, personal information, or API keys could be served to unintended clients
- Cache poisoning: Attackers can manipulate cached responses to serve malicious content to subsequent requests
- Denial of service: Malformed directives could cause the cache to behave unexpectedly, leading to performance degradation or crashes


The Vulnerability Explained

What Happens During the Attack

The undici cache interceptor is responsible for respecting HTTP caching semantics as defined in RFC 7234. The Cache-Control header contains directives that control caching behavior—directives like max-age=3600, private, no-store, etc.

The problem: When the cache interceptor receives a response with a malformed Cache-Control header—for example, one with improperly formatted directives or unexpected syntax—it fails to properly validate or reject the malformed input. Instead of treating the header as invalid and refusing to cache, the interceptor may:

  1. Partially parse the header, ignoring malformed portions while caching based on the valid portions
  2. Misinterpret directives, treating private as public or vice versa due to parsing errors
  3. Cache when it shouldn't, storing responses that should never be cached according to strict RFC compliance

Attack Scenario

Consider this real-world attack:

HTTP/1.1 200 OK
Cache-Control: max-age=3600, private=invalid-syntax, no-store
Content-Type: application/json

{
  "user_id": 12345,
  "api_token": "secret_token_xyz",
  "email": "user@example.com"
}

A strict parser should reject this header entirely because private=invalid-syntax is malformed (the private directive takes no parameters). However, if undici's cache interceptor doesn't validate this properly, it might:
- Ignore the malformed private=invalid-syntax directive
- Cache the response based on max-age=3600
- Result: Sensitive user data with an API token gets cached and served to other users or requests

An attacker could also craft headers that exploit the parsing logic in reverse:

Cache-Control: max-age=3600, public, no-store=ignored

If the parser processes directives left-to-right without proper precedence handling, it might cache the response (seeing max-age and public) while ignoring the no-store directive.

The Real Impact

For applications using undici (which is the HTTP client for many Node.js frameworks and tools):
- API responses containing authentication tokens could be cached and leaked to other users
- Personal data from API responses could persist in the cache longer than intended
- Cache poisoning attacks could serve stale or malicious data to clients
- Performance degradation if the cache behaves unexpectedly due to malformed directives


The Fix

What Changed

The fix involves upgrading undici from 7.28.0 to 7.29.0 (or 8.9.0 for v8 users). The upgrade includes stricter validation of Cache-Control header directives in the cache interceptor.

Before the fix (package.json and package-lock.json with undici 7.28.0):

"undici": {
  "version": "7.28.0",
  "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
  "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
}

After the fix (undici 7.29.0):

"undici": {
  "version": "7.29.0",
  "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
  "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
}

Additionally, a dependency override was added to package.json to ensure all transitive dependencies use the patched version:

{
  "homepage": "https://github.com/lovasoa/dezoomify",
  "overrides": {
    "undici": "7.29.0"
  }
}

Why These Changes Matter

  1. Version bump (7.28.0 → 7.29.0): The undici maintainers fixed the cache interceptor's validation logic to properly reject malformed Cache-Control directives. The new version implements RFC 7234 compliance more strictly.

  2. Dependency override: By adding the override, we ensure that even if other dependencies in the project specify an older version of undici, npm will use 7.29.0. This prevents transitive dependency conflicts from reintroducing the vulnerability.

  3. Integrity hash update: The sha512 hash changed because the package contents changed. This is expected and confirms we're using a different (patched) version.

How the Fix Prevents the Vulnerability

The patched version (7.29.0) includes:
- Stricter directive parsing: Malformed directives like private=invalid-syntax are now properly rejected
- Proper directive precedence: Directives are processed according to RFC 7234 specifications, preventing contradictory directives from causing unexpected behavior
- Validation of directive values: Parameters to directives are validated (e.g., max-age must be a numeric value)
- Fail-safe caching: If the Cache-Control header is invalid or malformed, the response is treated as non-cacheable by default

Now, when the cache interceptor encounters the malformed header from our attack scenario:

Cache-Control: max-age=3600, private=invalid-syntax, no-store

It will:
1. Parse max-age=3600 ✓ (valid)
2. Parse private ✓ (valid, no parameters expected)
3. Reject private=invalid-syntax ✗ (invalid syntax)
4. Reject the entire header as malformed and treat the response as non-cacheable

This ensures sensitive data is never cached inappropriately.


Prevention & Best Practices

1. Keep Dependencies Updated

  • Regularly update HTTP client libraries and other security-critical dependencies
  • Use tools like npm audit and npm update to identify and patch vulnerabilities
  • Consider automated dependency management tools like Dependabot

2. Implement Strict Cache-Control Validation

Even when using updated libraries, implement application-level validation:

// Example: Validate Cache-Control headers in your middleware
app.use((req, res, next) => {
  const originalSend = res.send;
  res.send = function(data) {
    const cacheControl = res.get('Cache-Control');

    // Reject responses with invalid Cache-Control headers
    if (cacheControl && !isValidCacheControl(cacheControl)) {
      res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
    }

    return originalSend.call(this, data);
  };
  next();
});

function isValidCacheControl(header) {
  // Implement RFC 7234 validation logic
  const directives = header.split(',').map(d => d.trim());
  return directives.every(d => /^[\w-]+=?[\w-]*$/.test(d));
}

3. Use Security Headers for Sensitive Data

For responses containing sensitive information, always set explicit cache directives:

// For authentication responses, API tokens, personal data
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
res.set('Pragma', 'no-cache');
res.set('Expires', '0');

4. Monitor Cache Behavior

  • Log cache hits/misses to detect anomalies
  • Monitor for unexpected cached responses
  • Implement cache invalidation strategies for sensitive data

5. Use Security Scanning Tools

  • Trivy: Scans for known CVEs in dependencies (as used in this fix)
  • npm audit: Built-in vulnerability scanner for npm packages
  • Snyk: Continuous vulnerability monitoring
  • OWASP Dependency-Check: Identifies known vulnerable components

6. Implement Defense in Depth

  • Don't rely solely on caching directives for security
  • Use authentication tokens with short expiration times
  • Implement server-side session management
  • Use HTTPS to prevent man-in-the-middle cache poisoning attacks

Key Takeaways

  • Cache-Control header injection is subtle: Malformed directives can bypass validation logic in HTTP clients, leading to unexpected caching behavior
  • Undici versions before 7.29.0 are vulnerable: If you're using undici 7.28.0 or earlier (or 8.x versions before 8.9.0), you must upgrade immediately
  • Dependency overrides are crucial: Using "overrides" in package.json ensures all transitive dependencies use the patched version, preventing version conflicts
  • Sensitive data requires explicit cache headers: Never rely on default caching behavior for responses containing authentication tokens or personal information
  • Static analysis caught this early: Security scanners like Trivy detected this vulnerability by matching package versions against the CVE database, preventing exploitation in production

How Orbis AppSec Detected This

Source: HTTP response headers from external APIs and services (specifically the Cache-Control header field)

Sink: The cache interceptor in undici's HTTP client library that processes the Cache-Control directive without strict RFC 7234 validation

Missing control: Insufficient validation of Cache-Control header syntax; malformed directives were not properly rejected, allowing them to influence caching decisions

CWE:
- CWE-345: Insufficient Verification of Data Authenticity
- CWE-444: Inconsistent Interpretation of HTTP Requests ('HTTP Request Smuggling')

Fix: Upgrade undici to version 7.29.0 or 8.9.0, which implements stricter Cache-Control header parsing and validation according to RFC 7234 specifications, and add a dependency override to ensure all transitive dependencies use the patched version.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-13697 demonstrates how subtle parsing vulnerabilities in HTTP libraries can lead to serious security issues. By mishandling malformed Cache-Control directives, undici's cache interceptor created an opportunity for information disclosure and cache poisoning attacks. The fix—upgrading to undici 7.29.0 or 8.9.0—implements stricter validation that prevents these attacks.

The key lesson for developers: never assume that third-party libraries handle untrusted input safely. Always keep dependencies updated, monitor security advisories, and implement application-level validation for critical security decisions like caching. By combining dependency management with defense-in-depth strategies, you can significantly reduce your attack surface.

If you're using undici in your Node.js applications, upgrade now. If you're using other HTTP clients, check their security advisories and ensure you're running the latest patched versions.


References

Frequently Asked Questions

What is a Cache-Control header injection?

It's an attack where an attacker crafts malformed Cache-Control directives to bypass cache validation logic, potentially causing the application to cache sensitive data or bypass security controls.

How do you prevent Cache-Control injection in Node.js?

Keep HTTP client libraries like undici updated, validate all HTTP headers on ingress, implement strict Cache-Control parsing, and use security headers like Cache-Control: no-store for sensitive responses.

What CWE is this vulnerability?

CWE-345 (Insufficient Verification of Data Authenticity) and CWE-444 (Inconsistent Interpretation of HTTP Requests) both apply, as the vulnerability stems from inconsistent parsing of HTTP directives.

Is upgrading undici enough to prevent this vulnerability?

Yes, upgrading to 7.29.0 or 8.9.0 patches the root cause. However, you should also use dependency overrides (as shown in the fix) to ensure all transitive dependencies use the patched version.

Can static analysis detect this vulnerability?

Yes, security scanners like Trivy detect this by checking package versions against known CVE databases. However, detecting the exploitation of this vulnerability at runtime would require HTTP header inspection and cache behavior monitoring.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #991

Related Articles

medium

How XML External Entity (XXE) Injection happens in Python and how to fix it

A high-severity XML External Entity (XXE) vulnerability was discovered in `utils/commands_extractors/find_java_repo_commands.py` where Python's native `xml.etree.ElementTree` library was used to parse potentially untrusted XML input. The fix replaces it with `defusedxml.ElementTree`, which disables external entity processing by default, preventing attackers from reading sensitive files or making unauthorized network requests.

critical

How XML Multiple Root Element Injection happens in Node.js and how to fix it

The foam3 project contained a critical vulnerability in xmldom version 0.6.0 that allowed attackers to create malformed XML documents with multiple root elements, violating the XML specification and potentially bypassing security validations. The fix removed the vulnerable xmldom dependency entirely from package.json and package-lock.json, eliminating the attack surface.

critical

How Prototype Pollution happens in Node.js and how to fix it

A critical prototype pollution vulnerability was discovered in `worker/import-core.js`, where `request.json()` parsed untrusted HTTP request bodies without filtering dangerous keys like `__proto__` and `constructor`. An attacker could send a crafted JSON payload to corrupt the global `Object` prototype, potentially affecting every object in the application runtime. The fix replaces the unsafe parse with a JSON reviver function that strips these dangerous keys before any object is constructed.

critical

How Server-Side Template Injection happens in Node.js EJS and how to fix it

CVE-2022-29078 is a critical server-side template injection (SSTI) vulnerability in EJS versions prior to 3.1.7, where the `outputFunctionName` option is passed directly into generated code without sanitization, allowing attackers to execute arbitrary JavaScript on the server. The fix upgrades the EJS dependency from 2.7.4 to 3.1.7+ (resolved here as 6.0.1), eliminating the unsafe code generation path. Any Node.js application rendering EJS templates with user-influenced options is at risk of ful

high

How Prototype Pollution happens in JavaScript via defu and how to fix it

CVE-2026-35209 is a high-severity prototype pollution vulnerability in the `defu` JavaScript library (versions prior to 6.1.5), where a crafted `__proto__` key in the defaults argument can corrupt the global Object prototype. The fix upgrades `defu` from 6.1.4 to 6.1.5 in `pnpm-lock.yaml` and enforces the version via a workspace override, closing the attack surface in production code that depends on `defu` for deep object merging.

high

How Denial of Service via infinite loop in nanoid happens in JavaScript and how to fix it

A high-severity denial of service vulnerability (CVE-2026-67213) was discovered in nanoid versions prior to 3.3.18, where the random ID generation function could enter an infinite loop, causing application hangs. The vulnerability was fixed by upgrading nanoid from 3.3.16 to 3.3.18 in both bun.lock and pnpm-lock.yaml, eliminating the infinite loop condition in the ID generation algorithm.