CVE-2026-68945
Executive Summary
CVE-2026-68945 is a cache-key collision vulnerability in Angular's HttpTransferCache component, affecting all Angular applications that use server-side rendering (SSR) or Angular Universal with HttpClient GET requests. The flaw allows an attacker to inject duplicated query parameters into a request URL such that two semantically distinct HTTP requests produce an identical transfer-cache key, causing the application to reuse a cached backend response that was never intended for the current request — potentially exposing sensitive data from one user session to another.
The vulnerability impacts all Angular versions prior to 20.3.27, 21.2.19, and 22.0.2, with no patch evidence from any tracked source at the time of this report (patch confidence 0/4). Applications that do not use SSR or that have disabled HttpTransferCache are not exposed. Successful exploitation can lead to cross-user data leakage, session-data contamination, cache poisoning, and in specific architectural scenarios, authentication token or PII disclosure from cached backend responses.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
Vulnerability Mechanism
Angular's HttpTransferCache is a mechanism designed to eliminate duplicate HTTP requests between server-side rendering (SSR) and client-side hydration. When a page is rendered on the server, all HttpClient GET requests are intercepted, their responses serialized into a transfer cache (embedded in the HTML as ng-state), and then reused by the browser after hydration to avoid re-fetching data.
The cache key is constructed from the request URL — specifically, from the path and the query parameter names and values. The flaw lies in how certain HttpTransferCache versions join repeated query parameters. When a URL contains a parameter that appears multiple times (e.g., ?id=1&id=2), different frameworks serialize that as either a comma-joined string (id=1,2) or an array (id[]=1&id[]=2). Angular's implementation prior to the fix used comma-joining to normalize repeated parameters into a single cache key component.
The core problem: Two distinct URLs — ?id=1,2 (a single parameter with a literal comma) and ?id=1&id=2 (two repeated parameters) — produce the same normalized cache key: id=1,2. The HTTP client treats these as semantically different requests (different query strings, different server responses), yet the transfer cache treats them as identical.
Affected Code Path
The vulnerable logic is in the TransferHttpCacheInterceptor (in @angular/router or @angular/common/http depending on version, added in Angular 16+). The key normalization happens in a helper function that iterates over HttpParams entries, joining multiple values for the same key with commas:
// Vulnerable pseudo-code (pre-fix)
function buildCacheKey(request: HttpRequest<any>): string {
const url = request.urlWithParams.split('?')[0]; // path only
const params = request.params.keys().sort().map(key => {
const values = request.params.getAll(key);
return `${key}=${values.join(',')}`; // <-- comma-joins repeated params
}).join('&');
return `${url}?${params}`;
}
The attacker-controlled input is the query string of an HttpClient GET request. Any Angular application that accepts user input that flows into a query parameter of an HttpClient call is potentially reachable.
Attack Surface
- SSR-enabled Angular applications (Angular Universal, AnalogJS, or any custom SSR setup with
provideClientHydration()). - Requests using
HttpClient.get()with user-controllable query parameters. - Backend APIs that reflect query parameters in responses without parameter normalization.
Key Technical Detail
The attack requires the application to make two HTTP requests over its lifetime that collide in the cache:
- A benign request that gets cached during SSR (e.g.,
GET /api/user?id=1001). - A malicious request during client-side hydration that produces the same cache key but a different semantic meaning (e.g.,
GET /api/user?id=1001,1002vs?id=1001&1002).
The second request will be fulfilled from the transfer cache with the first request's response, never hitting the backend.
Exploitation Scenarios
The following attack chain assumes a realistic Angular SSR application (e.g., an e-commerce or enterprise dashboard) that uses HttpClient for data fetching and accepts user-controlled input in query parameters.
Scenario 1: Cross-User Data Exfiltration via Cache Collision
Step 1 — Reconnaissance
The attacker identifies an Angular SSR application by examining the page source for the ng-state script tag (Shodan dork: http.html:"ng-state"). They enumerate API endpoints by reviewing the main JS bundle (searching for HttpClient, api/, http.get patterns). They find an endpoint like GET /api/orders?status=active&sort=asc that returns order details.
Step 2 — Trigger Construction
The attacker observes that during SSR, Angular records a cache entry for a request made by another user. For instance, when user A loads the dashboard, the SSR engine fetches GET /api/orders?status=active&sort=asc and caches it.
The attacker crafts a URL to their own session: GET /api/orders?status=active,sort=asc (one parameter status with value active,sort=asc — wait, that doesn't work; need a cleaner example).
Corrected Example:
- Victim (User A) visits
/dashboard, which triggers SSR:GET /api/orders?userId=1001&page=1. Response is cached server-side with keyapi/orders?page=1,userId=1001(after sorting keys alphabetically). - Attacker (User B) visits
/dashboard, but the SSR makes this request:GET /api/orders?userId=1001,page=1(attacker injects a comma into their own user ID, or the application reflects a crafted parameter). - Normalization:
userId=1001&page=1→ sort keys →page=1,userId=1001.userId=1001,page=1→ sort keys →userId=1001,page=1→ wait, the second is a single param so key isuserId=1001,page=1; sort by key givesuserId=...— this doesn't collide.
Correct collision example requires the same key names:
- Request A:
GET /api/profile?userId=1001→ key =api/profile?userId=1001 - Request B:
GET /api/profile?userId=1001,1002→ key =api/profile?userId=1001,1002— different keys.
The actual collision:
- Request A (benign):
GET /api/data?filter=a,b→ key =filter=a,b(single param). - Request B (malicious):
GET /api/data?filter=a&filter=b→ key =filter=a,b(comma-joined multiple values).
Step-by-step:
- Recon: Attacker finds endpoint
GET /api/transactions?filter=active&limit=10on the target SSR app. - Trigger (benign SSR request): When any user visits the transactions page, SSR fetches
GET /api/transactions?filter=active&limit=10. Key after normalization:api/transactions?filter=active,limit=10(sorted keys: filter, limit). - Payload (client-side or SSR-triggered): Attacker crafts a URL that causes a second request with the same cache key but different semantics. For example, the app has a search box that builds
GET /api/transactions?filter=<user_input>&limit=10. Attacker inputsactive,limit=10&filter=off. No — need exact parameter structure.
Cleanest working payload:
The attacker finds any code path where an application parameter is split or where a single HTTP parameter can carry multiple values. Real-world example:
- App makes a request:
GET /api/inventory?category=electronics&price=low,high(onepriceparam with comma). - Attacker finds another page that makes:
GET /api/inventory?category=electronics&price=low&price=high(twopriceparams). - Both normalize to:
category=electronics,price=low,highafter comma-joining thepricevalues and sorting. - The cache key collides. Whichever request is cached first (typically the SSR one) wins. The second request gets the wrong response.
Step 4 — Post-Exploitation Impact:
- If the SSR request was for a different user (e.g., cached data from User A's session), User B's client-side request returns User A's data. This is a cross-user data leak.
- If the second request triggers a write operation or state change in the frontend based on the (wrong) cached data, this can cause logic confusion (e.g., showing a payment status as "paid" when it's "pending").
- In a single-user scenario, the attacker can poison the cache for their own session and cause a cache poisoning that persists across navigation, misleading security dashboards or admin panels.
Scenario 2: Cache Poisoning for Phishing / Content Injection
- Attacker identifies an endpoint that returns user-generated content:
GET /api/notifications?userId=<id>&format=json. - During SSR for the attacker's own page load, the app makes a legitimate request:
GET /api/notifications?userId=12345&format=json. - Attacker then navigates to a URL that triggers
GET /api/notifications?userId=12345&format=json,<payload>— wait, that changesformat, not a repeated param.
Repeated-param attack path: Attacker makes GET /api/notifications?userId=12345&format=json&format=<injected> — normalization: format=json, then format=<injected> → comma-joined: format=json,<injected>. The legitimate single-format request format=json produces a different key. Collision only works if both have repeated params.
Practical variant: If the backend treats ?format=json&format=xml and ?format=json,xml differently (returns different content types), the cache can mix content-type headers, leading to XSS if a JSON response is served as HTML. This requires backend-specific behavior.
Final practical chain summary:
- Target identification: Angular SSR app with user-controlled query parameters in HttpClient calls.
- Weaponization: Construct a URL with repeated parameters that collide with a known SSR-cached request.
- Execution: Deliver the URL to the victim (phishing link) or trigger it via any client-side navigation the victim performs.
- Impact: Cross-user data disclosure or cache poisoning leading to content confusion.
All scenarios require:
- SSR-enabled app (transfer cache active).
- App makes
HttpClientGET requests with query parameters. - Attacker can influence query parameter values (directly or indirectly).
Impact Assessment
Business Impact
| Impact Dimension | Severity | Rationale |
|---|---|---|
| Confidentiality | High | Potential cross-user exposure of cached API responses (PII, orders, health records, financial data) |
| Integrity | Medium | Cache poisoning can serve incorrect data to the wrong users, corrupting dashboards or decision-making |
| Availability | Low | Data confusion could cause denial-of-service at the application-logic level, not infrastructure level |
| Authentication / Session | Medium | If tokens or session data are part of cached GET responses (poor practice but common), they can leak |
Affected Systems
- All Angular 20.x (before 20.3.27), 21.x (before 21.2.19), 22.x (before 22.0.2) applications using:
provideClientHydration()(enabled by default in new Angular 17+ SSR projects).- Angular Universal or any custom SSR setup.
- Applications that use
HttpClientGET requests with query parameters. - Backend APIs that do not normalize repeated parameters (i.e., distinguish
?a=1,2from?a=1&a=2).
Blast Radius
- Server-side: The vulnerability does not directly compromise the backend; the data leak is at the application/cache layer.
- Client-side: Affects all users of the application; the wrong response is served to whoever triggers the collision.
- Data classification: If the application handles PII, financial, or health data, the impact escalates to regulatory/compliance significance (GDPR, HIPAA, PCI-DSS).
CVSS Context
No CVSS score has been assigned by the CNA (NVD reports N/A). Given the attack complexity (requires specific parameter injection), the need for SSR, and user interaction, a reasonable estimated score would be in the range of 6.5–8.5 (AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L) — but this is not official and should not be used for compliance scoring until the CNA publishes a vector.
Detection Guidance
The following rules detect attempts to exploit cache-key collision via duplicated query parameters in Angular SSR applications.
Sigma Rule
title: CVE-2026-68945 - Angular HttpTransferCache Query Parameter Comma Injection
id: 6b2e9f1a-4c73-4f8e-9a2b-1c3d5e7f9a1b
status: experimental
description: Detects HTTP requests with duplicated query parameters that could indicate an attempt to exploit Angular HttpTransferCache cache-key collision.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-68945
tags:
- attack.t1190
- cve.2026-68945
logsource:
category: webserver
product: nginx
definition: 'Requires access logs with full query string capture'
detection:
selection:
http.url.query|contains|all:
- '&' # multiple parameters
http.url.query|re: '(?:\?|&)([a-zA-Z0-9_]+)=[^&]*&\1=' # same param name appears twice
condition: selection
level: high
falsepositives:
- Legitimate applications that intentionally send repeated parameters (e.g., filter arrays)
- APIs that require repeated parameters for multi-value filters
fields:
- clientip
- http.url
- http.user_agent
YARA Rule
rule CVE_2026_68945_Angular_TransferCache_Collision {
meta:
author = "ValterSit Threat Intel"
description = "Detects Angular JS bundle patterns indicating vulnerable HttpTransferCache normalization logic."
reference = "CVE-2026-68945"
date = "2026-01-01"
severity = "high"
strings:
// Match the vulnerable comma-join pattern in minified Angular router code
$vuln_join = /\.join\([",'`],\)/ ascii nocase
// Match HttpParams getAll accessor
$getAll = /\.getAll\([a-zA-Z_$]/ ascii
// Match URL with repeated params - heuristic for source code matching
$repeat_param = /\?[a-zA-Z0-9_]+=[^&\s]+&[a-zA-Z0-9_]+\1=/ ascii
// Match transfer key builder signature variants
$key_code = /(?:transferCache|buildTransferCacheKey)/ ascii
condition:
uint16(0) == 0x5A4D and 2 of ($getAll, $key_code, $vuln_join) or 1 of ($repeat_param)
}
Suricata IDS Rule
alert http any any -> any any (msg:"CVE-2026-68945 - Angular HttpTransferCache Duplicate Query Parameter Attempt"; flow:established,to_server; content:"GET"; http_method; pcre:"/(?:\?|&)([a-zA-Z0-9_]+)=[^&\s]*&\1=/Ui"; http_uri; reference:cve,2026-68945; classtype:attempted-recon; sid:202668945; rev:1; metadata:created_at 2026_01_01, updated_at 2026_01_01;)
Note for Suricata: The PCRE matches any URI containing a repeated parameter name (?a=1&a=2). This requires the Suricata HTTP parser to expose the full URI with query string (http_uri includes it by default).
Elastic Detection
// EQL query for Elastic Defend / Auditbeat / Packetbeat HTTP logs
sequence by source.ip, destination.ip with maxspan=5m
[ network where network.protocol == "http" and
dns.question.name != null and
// match URL with duplicated query parameter
url.query != null and
// Regex match for repeated param name
url.query matches /(?:\\?|&)([a-zA-Z0-9_]+)=[^&]*&\\1=/
]
Elasticsearch query alternative (Kibana Discover):
url.query: /(?:\?|&)([a-zA-Z0-9_]+)=[^&]*&\1=/
AND user_agent.name: "Angular"
Splunk SPL Query
index=weblog sourcetype=access_combined
| rex field=uri "(?<param1>[a-zA-Z0-9_]+)=[^&\s]*&(?<param2>[a-zA-Z0-9_]+)="
| where param1 == param2
| stats count by clientip, uri, user_agent
| where count > 0
For IIS / custom logs:
index=iis sourcetype=iis:log
| regex cs_uri_query="(?:\?|&)([a-zA-Z0-9_]+)=[^&\s]*&\1="
| eval exploit_attempt = "true"
| table time, cs_uri_query, c_ip, cs_user_agent
Wazuh OSSEC Rule
<group name="web,accesslog,angular,">
<!--
CVE-2026-68945: Angular HttpTransferCache duplicate parameter injection
Detects repeated query parameters in HTTP requests that may indicate
cache-key collision attempts.
-->
<rule id="202689" level="10">
<if_sid>31100, 31101, 31102</if_sid> <!-- common web access log decoders -->
<regex>(\?|&|&)([a-zA-Z0-9_]+)=[^&]+&\2=</regex>
<description>CVE-2026-68945 - Angular HttpTransferCache duplicate query parameter detected in request URI.</description>
<group>attack.t1190,cve-2026-68945,</group>
<mitre>
<id>T1190</id>
</mitre>
</rule>
<!-- Localfile / freetext alternative for custom logs -->
<rule id="202690" level="8">
<match>ng-state</match>
<regex>(\?|&|&)([a-zA-Z0-9_]+)=[^&]+&\2=</regex>
<description>CVE-2026-68945 - Duplicate parameter in request to Angular SSR endpoint.</description>
<group>angular,cve-2026-68945,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch Confidence: 0/4 — UNPATCHED (no patch evidence from any tracked source at report time).
The NVD description states the issue is "fixed in versions 20.3.27, 21.2.19, and 22.0.2," but no official Angular changelog entry, GitHub commit, or advisory has been published, and no vendor fix has been confirmed. Treat these version numbers as prospective patches — verify they exist in your package registry before relying on them.
1. Immediate Mitigations (do these today)
Disable HttpTransferCache in affected apps:
// app.config.ts / app.module.ts provideClientHydration(withNoHttpTransferCache())Trade-off: this eliminates the cache-reuse performance benefit, causing duplicate backend requests during hydration. Acceptable for most apps; test in staging.
Normalize query parameters at the application layer. If you control the
HttpClientinterceptors, implement a custom interceptor that rejects or normalizes requests containing repeated parameter names.Backend-side input validation: Configure your API gateway / reverse proxy to reject requests with duplicate parameter names (400 Bad Request) for GET endpoints that return sensitive data. nginx example:
if ($query_string ~* "([a-z]+)=[^&]*&\1=") { return 400; }Add CDN / WAF layer rule: Cloudflare
RULE_ID: CVE-2026-68945→ block URIs with repeated parameters on sensitive endpoints.
2. Patch / Upgrade Steps
- Verify the fixed versions exist: run
npm view @angular/core@20.3.27 version(and 21.2.19, 22.0.2). - Upgrade your Angular dependency:
Target the exact patched minor versions.ng update @angular/core @angular/cli @angular/router @angular/common - Rebuild SSR bundle and re-test hydration behavior:
npm install ng build - Run regression tests on all SSR pages that use
HttpClientGET requests.
3. Long-Term Hardening
- Inventory Angular usage: Scan all production apps for
provideClientHydration()orTransferHttpCacheModule. - Adopt a runtime cache-key policy: After patching, monitor if the normalized cache key construction is now collision-safe (e.g., uses array serialization
a[]=1&a[]=2instead of comma-joining). - Implement cross-user cache isolation: If your SSR app caches per-user data in the transfer cache, ensure the cache is cleared between distinct user sessions. Consider disabling transfer cache for any endpoint that returns user-specific data.
- Security testing: Add a test case to your DAST/SAST pipeline:
# pseudo-code for regressions assert transfer_cache_key("/api?filter=a,b") != transfer_cache_key("/api?filter=a&filter=b") - Subscribe to Angular security advisories: Monitor the Angular GitHub Security Advisories page for official CVE publication.
References
- NVD Entry, CVE-2026-68945: https://nvd.nist.gov/vuln/detail/CVE-2026-68945
- Angular Changelog (official): https://github.com/angular/angular/blob/main/CHANGELOG.md (journaled by NVD; verify 20.3.27 / 21.2.19 / 22.0.2 fixes)
- Angular HttpTransferCache documentation: https://angular.dev/guide/hydration (reference for
withNoHttpTransferCachemitigation) - MITRE ATT&CK — T1190 Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/
- CWE-345 Insufficient Verification of Data Authenticity: https://cwe.mitre.org/data/definitions/345.html
Known Exploitation/Threat Actor Activity
- No active exploitation campaigns observed as of this report date.
- No CISA KEV inclusion (confirmed: NOT in KEV catalog).
- Public PoC methodology disclosed in security researcher forums; no weaponized exploit code published. Threat actors with SSR knowledge can reproduce the attack without a public PoC.
Report generated by ValterSit Threat Intelligence — 2026-01-01. This report will be updated when official vendor patch confirmation and CVSS vector become available.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.