CVE-2026-6907
Medium · 4.3CVSS
4.3
EPSS
0.4%
Published
May 5, 2026
CISA due date
—
Cloudanix analysis
Some Django websites may accidentally save private pages and show them to the wrong visitor.
- Exploitation
- Requires User Interaction
- Attacker needs
- User Interaction
- Complexity
- Moderate
- Impact
- Data Exposure
- Component
- Web Framework
- Class
- Information Disclosure
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
Django's site-wide cache middleware incorrectly stores HTTP responses when the response contains `Vary: *`. Under HTTP caching semantics, `Vary: *` means the response is not reusable for later requests because it may depend on unspecified request attributes. In affected Django versions, `UpdateCacheMiddleware` can cache such responses anyway, allowing `FetchFromCacheMiddleware` to later serve a response generated for one request context to a different user or unauthenticated requester. The impact is information disclosure of response bodies, headers, or personalized content that an application expected would not be cached.
Why it matters to the business
Applications using Django's site-wide cache middleware may unintentionally expose private user data, account-specific pages, or other sensitive response content to the wrong person. This can create privacy incidents, regulatory reporting obligations, customer trust impact, and operational work to purge caches and investigate what was served. The vulnerability is not currently in CISA KEV, has low EPSS, and has no known public exploit maturity in the provided data, but it has been public for 97 days, so internet-facing Django applications with cache middleware enabled should still be reviewed and upgraded.
How an attack unfolds
An attacker looks for a Django application that uses the framework's cache middleware and has endpoints returning sensitive, user-specific content with a `Vary: *` header. Because affected Django versions mishandle that header, a response that should be considered non-reusable can be stored in the configured cache backend. Once the private response is cached, a subsequent request for the same cache key can receive the previously stored content even if the requester is a different user or is unauthenticated. In practical exploitation, the attacker must identify a cacheable route, influence or wait for a sensitive response to be generated and cached, and then request the same resource to retrieve cached content. The issue is most dangerous where site-wide caching is applied broadly to authenticated or semi-private pages and where cache keys do not adequately separate users, cookies, authorization state, or other request-specific inputs.
How to fix it
Upgrade Django to a fixed release: use Django 6.0.5 or later for the 6.0 series, or Django 5.2.14 or later for the 5.2 series. Unsupported Django branches such as 5.0.x, 4.1.x, and 3.2.x were not evaluated and may also be affected; migrate them to a supported fixed branch rather than relying on compensating controls. After upgrading, purge application cache backends, reverse-proxy caches, CDN caches, and any file/database cache stores that may contain incorrectly cached private responses. If immediate upgrade is not possible, disable Django site-wide cache middleware for authenticated or sensitive routes, avoid caching responses with `Vary: *`, ensure private responses include appropriate `Cache-Control: private, no-store` semantics, and configure upstream caches not to store authenticated responses.
How to confirm the fix worked
Confirm the deployed Django version is 6.0.5 or later, or 5.2.14 or later, and verify unsupported branches have been removed. Review application settings for `django.middleware.cache.UpdateCacheMiddleware`, `django.middleware.cache.FetchFromCacheMiddleware`, or `django.middleware.cache.CacheMiddleware`; exposure requires use of this caching path or equivalent site-wide caching behavior. In a test environment, create or identify a response that includes `Vary: *`, request it in one session, then request the same URL in a separate session or unauthenticated context and confirm the first response is not served from cache. Inspect the configured cache backend after the test to confirm entries with `Vary: *` responses are not stored. Also validate that authenticated and private responses are not cached by CDN, reverse proxy, or application cache layers.
Typical attack path
- Identify a Django application that appears to use site-wide caching or returns cache-related headers.
- Find endpoints that return personalized or private content and include `Vary: *` in the response.
- Cause or wait for a sensitive response from an authenticated user or privileged request context to be generated.
- Rely on vulnerable Django cache middleware to incorrectly store that response despite the `Vary: *` header.
- Request the same URL or cache key from another session or unauthenticated context.
- Receive the cached private response that should not have been reused.
How to detect it
To determine whether this was already exploited, review web access logs, application logs, cache backend contents, and CDN/reverse-proxy cache logs for responses containing `Vary: *` that were later served as cache hits to different users, sessions, IP addresses, or unauthenticated requests. Focus on authenticated endpoints, account pages, profile views, dashboards, billing pages, admin-adjacent pages, or APIs returning user-specific data. Look for the same URL receiving a private 200 response followed by cache-hit responses without the same cookies, authorization headers, or session identity. If available, correlate `Set-Cookie`, `Cookie`, `Authorization`, `Vary`, `Cache-Control`, `Age`, `X-Cache`, `X-Cache-Status`, `CF-Cache-Status`, `X-Served-By`, and application user/session identifiers. Inspect Redis, Memcached, database cache tables, local-memory debug dumps, or filesystem cache directories for cached response bodies containing personal data, session-derived content, or headers with `Vary: *`. Absence of explicit cache-hit logging does not prove non-exploitation; in that case, prioritize version/configuration validation and cache purging.
- Vary: *
- django.middleware.cache.UpdateCacheMiddleware
- django.middleware.cache.FetchFromCacheMiddleware
- django.middleware.cache.CacheMiddleware
- CACHE_MIDDLEWARE_ALIAS
- CACHE_MIDDLEWARE_SECONDS
- CACHE_MIDDLEWARE_KEY_PREFIX
- X-Cache: HIT
- X-Cache-Status: HIT
- CF-Cache-Status: HIT
- Age:
- Set-Cookie:
- Cache-Control: private
- Cache-Control: no-store
What your security team should do
- Inventory Django applications and prioritize those using site-wide cache middleware on internet-facing or authenticated services.
- Upgrade affected Django deployments to 6.0.5 or later, or 5.2.14 or later, as applicable.
- Migrate unsupported Django versions to a supported fixed release because older series may also be affected.
- Purge all relevant cache layers after patching, including Django cache backends, reverse proxies, CDNs, and file/database cache stores.
- Search logs and cache contents for evidence that responses with `Vary: *` were cached or served as cache hits.
- Review sensitive routes to ensure authenticated, user-specific, or private responses are not covered by broad site-wide caching.
- Add regression tests for cache behavior involving `Vary: *`, authenticated requests, cookies, and authorization headers.
The record
An issue was discovered in 6.0 before 6.0.5 and 5.2 before 5.2.14. `django.middleware.cache.UpdateCacheMiddleware` erroneously caches requests where the `Vary` header contained an asterisk (`'*'`). This can lead to private data being stored and served. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django would like to thank Ahmad Sadeddin for reporting this issue.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|---|---|---|
| openSUSE:Tumbleweed | python-Django | 0 | 5.2.14-1.1 |
| openSUSE:Tumbleweed | python-Django4 | 0 | 4.2.30-2.1 |
| openSUSE:Tumbleweed | python-Django6 | 0 | 6.0.5-1.1 |
Related vulnerabilities
- CVE-2026-7666 An issue was discovered in Django 6.0 before 6.0.6 and 5.2 before 5.2.15. Also affects python-Django
- CVE-2026-8404 An issue was discovered in Django 5.2 before 5.2.15 and 6.0 before 6.0.6. Also affects python-Django
- CVE-2026-6873 An issue was discovered in Django 6.0 before 6.0.6 and 5.2 before 5.2.15. Also affects python-Django
- CVE-2026-48587 An issue was discovered in Django 5.2 before 5.2.15 and 6.0 before 6.0.6. Also affects python-Django
- CVE-2026-35193 An issue was discovered in Django 5.2 before 5.2.15 and 6.0 before 6.0.6. Also affects python-Django
- CVE-2026-5766 An issue was discovered in 6.0 before 6.0.5 and 5.2 before 5.2.14. Also affects python-Django
- CVE-2026-35192 An issue was discovered in 6.0 before 6.0.5 and 5.2 before 5.2.14. Also affects python-Django
- CVE-2026-3902 An issue was discovered in 6.0 before 6.0.4, 5.2 before 5.2.13, and 4.2 before 4.2.30. Also affects python-Django