GHSA-ff3f-86qr-9cv3High

Angular Server-Side Rendering (SSR): Denial of Service via Numeric URL Matrix Parameters

Published
September 30, 2026
Last Modified
September 30, 2026

🔗 CVE IDs covered (1)

📋 Description

A denial of service (DoS) vulnerability was identified in @angular/router when Server-Side Rendering (SSR) is enabled on Node.js (V8).

When @angular/router parses incoming request URLs, it extracts path segments, matrix parameters, and child outlets into plain JavaScript objects (Record<string, string>). When matrix parameter names or outlet names are numeric strings (such as /a;990;2522), the V8 JavaScript engine interprets them as array-indexed properties rather than named properties.

Under V8's internal property-storage heuristics, setting numeric keys on an initially empty object causes V8 to allocate a dense array backing store (HOLEY_ELEMENTS) sized to the maximum index rather than falling back to sparse dictionary storage. Specifically, assigning sequential or moderately large numeric keys (like 990 followed by 2522) causes V8 to allocate a contiguous backing store of ~2,522 pointers (~20 KB to 25 KB of heap) for a single 11-byte segment.

Because each segment in a URL path allocates its own independent parameters object, an attacker can craft URLs with repeated numeric matrix parameters to achieve an asymmetric memory amplification factor of approximately ~350x.

Impact

Successful exploitation allows an unauthenticated remote attacker to exhaust the Node.js old-space heap with modest request volume, terminating the SSR worker with an unrecoverable JavaScript heap out of memory fatal error and causing a Denial of Service.

  • High Amplification: A single 11-byte segment (/a;990;2522) consumes ~20 KB–25 KB of V8 heap.
  • Low Concurrency Required:
    • With 8 KB request paths (~740 segments, within default Nginx 8 KB buffer limits), as few as 12–22 concurrent requests crash a 256 MiB–512 MiB Node.js SSR worker.
    • With smaller 1 KB–2 KB request paths (~90–180 segments), a burst of ~50–100 concurrent requests achieves the same heap exhaustion.
  • Client-side SPAs Unaffected: Pure client-side Angular applications (Single Page Applications without SSR) are not vulnerable, as local browser memory consumption does not cross a security boundary.

Attack Preconditions & Vulnerable Configurations

An application is affected only if all of the following conditions are met:

  • SSR Enabled: The application runs in a Server-Side Rendering environment powered by Node.js / V8.
  • Direct Router Parsing: User-controlled request URLs are parsed by @angular/router during SSR.
  • No Reverse-Proxy Semicolon/Segment Filtering: Upstream reverse proxies (Nginx, Cloudflare, ALB) forward URLs containing semicolons (;) and multiple path segments without stripping or rejecting them.

Exploit Payload Example

An attacker sends concurrent HTTP requests with repeated numeric matrix parameters:

GET /a;990;2522/a;990;2522/a;990;2522/... HTTP/1.1
Host: example.com

Even with paths under 2 KB, overlapping requests during SSR will rapidly consume the V8 heap until the process crashes.

Patches

The issue is resolved by updating @angular/router to enforce V8 dictionary elements storage (setUrlDerivedKey) for numeric URL-derived keys (index >= 32). This prevents V8 from allocating oversized contiguous array backing stores while preserving route matching, parameter values, and component input bindings.

  • 22.2.0
  • 21.2.24
  • 20.3.32

Workarounds & Mitigations

If you cannot immediately upgrade to a patched version, apply one of the following mitigations at your edge or reverse proxy:

  1. Block or Sanitize Matrix Parameters at the Reverse Proxy:
    Configure your reverse proxy (e.g., Nginx, Cloudflare, or AWS WAF) to reject or strip semicolons (;) in request paths before forwarding requests to the Angular SSR service:
    # Nginx example: reject requests containing matrix parameters
    if ($uri ~* ";") {
        return 400;
    }
    
  2. Enforce Strict Path Segment Limits:
    Reject requests with excessive path depth (e.g., more than 20–30 segments).
  3. Increase Node.js Old Space:
    Increase --max-old-space-size (e.g., to 2048 or 4096 MB) to increase the concurrency threshold required to exhaust memory, though this does not fully eliminate the vulnerability under sustained traffic.

🎯 Affected products4

  • npm/@angular/router:>= 22.0.0, < 22.2.0
  • npm/@angular/router:>= 21.0.0, < 21.2.24
  • npm/@angular/router:>= 20.0.0, < 20.3.32
  • npm/@angular/router:<= 19.2.25

🔗 References (7)