Skip to content

Commit 5665800

Browse files
1 parent 1410036 commit 5665800

2 files changed

Lines changed: 304 additions & 0 deletions

File tree

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-x288-3778-4hhx",
4+
"modified": "2026-02-25T22:42:36Z",
5+
"published": "2026-02-25T22:42:36Z",
6+
"aliases": [
7+
"CVE-2026-27739"
8+
],
9+
"summary": "Angular SSR is vulnerable to SSRF and Header Injection via request handling pipeline",
10+
"details": "A [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) vulnerability has been identified in the Angular SSR request handling pipeline. The vulnerability exists because Angular’s internal URL reconstruction logic directly trusts and consumes user-controlled HTTP headers specifically the Host and `X-Forwarded-*` family to determine the application's base origin without any validation of the destination domain.\n\nSpecifically, the framework didn't have checks for the following:\n- **Host Domain**: The `Host` and `X-Forwarded-Host` headers were not checked to belong to a trusted origin. This allows an attacker to redefine the \"base\" of the application to an arbitrary external domain.\n- **Path & Character Sanitization**: The `X-Forwarded-Host` header was not checked for path segments or special characters, allowing manipulation of the base path for all resolved relative URLs.\n- **Port Validation**: The `X-Forwarded-Port` header was not verified as numeric, leading to malformed URI construction or injection attacks.\n\n\nThis vulnerability manifests in two primary ways:\n\n- **Implicit Relative URL Resolution**: Angular's `HttpClient` resolves relative URLs against this unvalidated and potentially malformed base origin. An attacker can \"steer\" these requests to an external server or internal service.\n- **Explicit Manual Construction**: Developers injecting the `REQUEST` object to manually construct URLs (for fetch or third-party SDKs) directly inherit these unsanitized values. By accessing the `Host` / `X-Forwarded-*` headers, the application logic may perform requests to attacker-controlled destinations or malformed endpoints.\n\n### Impact\n\nWhen successfully exploited, this vulnerability allows for arbitrary internal request steering. This can lead to:\n- **Credential Exfiltration**: Stealing sensitive `Authorization` headers or session cookies by redirecting them to an attacker's server.\n- **Internal Network Probing**: Accessing and transmitting data from internal services, databases, or cloud metadata endpoints (e.g., `169.254.169.254`) not exposed to the public internet.\n- Confidentiality Breach: Accessing sensitive information processed within the application's server-side context.\n\n### Attack Preconditions\n\n- The victim application must use Angular SSR (Server-Side Rendering).\n- The application must perform `HttpClient` requests using relative URLs OR manually construct URLs using the unvalidated `Host` / `X-Forwarded-*` headers using the `REQUEST` object.\n- **Direct Header Access**: The application server is reachable by an attacker who can influence these headers without strict validation from a front-facing proxy.\n- **Lack of Upstream Validation**: The infrastructure (Cloud, CDN, or Load Balancer) does not sanitize or validate incoming headers.\n\n### Patches\n\n- 21.2.0-rc.1\n- 21.1.5\n- 20.3.17\n- 19.2.21\n\n\n### Workarounds\n- **Use Absolute URLs:** Avoid using `req.headers` for URL construction. Instead, use trusted variables for your base API paths.\n- **Implement Strict Header Validation (Middleware)**: If you cannot upgrade immediately, implement a middleware in your `server.ts` to enforce numeric ports and validated hostnames.\n\n```ts\nconst ALLOWED_HOSTS = new Set(['your-domain.com']);\n\napp.use((req, res, next) => {\n const hostHeader = (req.headers['x-forwarded-host'] ?? req.headers['host'])?.toString();\n const portHeader = req.headers['x-forwarded-port']?.toString();\n\n if (hostHeader) {\n const hostname = hostHeader.split(':')[0];\n // Reject if hostname contains path separators or is not in allowlist\n if (/^[a-z0-9.:-]+$/i.test(hostname) || \n (!ALLOWED_HOSTS.has(hostname) && hostname !== 'localhost')) {\n return res.status(400).send('Invalid Hostname');\n }\n }\n\n // Ensure port is strictly numeric if provided\n if (portHeader && !/^\\d+$/.test(portHeader)) {\n return res.status(400).send('Invalid Port');\n }\n\n next();\n});\n```\n\n### References\n\n- [Fix](https://github.com/angular/angular-cli/pull/32516)\n- [Docs](https://angular.dev/best-practices/security#preventing-server-side-request-forgery-ssrf)",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "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"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "@angular/ssr"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "21.2.0-next.0"
29+
},
30+
{
31+
"fixed": "21.2.0-rc.1"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "< 21.2.0-rc.0"
38+
}
39+
},
40+
{
41+
"package": {
42+
"ecosystem": "npm",
43+
"name": "@angular/ssr"
44+
},
45+
"ranges": [
46+
{
47+
"type": "ECOSYSTEM",
48+
"events": [
49+
{
50+
"introduced": "21.0.0-next.0"
51+
},
52+
{
53+
"fixed": "21.1.5"
54+
}
55+
]
56+
}
57+
]
58+
},
59+
{
60+
"package": {
61+
"ecosystem": "npm",
62+
"name": "@angular/ssr"
63+
},
64+
"ranges": [
65+
{
66+
"type": "ECOSYSTEM",
67+
"events": [
68+
{
69+
"introduced": "20.0.0-next.0"
70+
},
71+
{
72+
"fixed": "20.3.17"
73+
}
74+
]
75+
}
76+
]
77+
},
78+
{
79+
"package": {
80+
"ecosystem": "npm",
81+
"name": "@angular/ssr"
82+
},
83+
"ranges": [
84+
{
85+
"type": "ECOSYSTEM",
86+
"events": [
87+
{
88+
"introduced": "0"
89+
},
90+
{
91+
"fixed": "19.2.21"
92+
}
93+
]
94+
}
95+
]
96+
},
97+
{
98+
"package": {
99+
"ecosystem": "npm",
100+
"name": "@nguniversal/common"
101+
},
102+
"ranges": [
103+
{
104+
"type": "ECOSYSTEM",
105+
"events": [
106+
{
107+
"introduced": "0"
108+
},
109+
{
110+
"last_affected": "16.2.0"
111+
}
112+
]
113+
}
114+
]
115+
},
116+
{
117+
"package": {
118+
"ecosystem": "npm",
119+
"name": "@nguniversal/express-engine"
120+
},
121+
"ranges": [
122+
{
123+
"type": "ECOSYSTEM",
124+
"events": [
125+
{
126+
"introduced": "0"
127+
},
128+
{
129+
"last_affected": "16.2.0"
130+
}
131+
]
132+
}
133+
]
134+
}
135+
],
136+
"references": [
137+
{
138+
"type": "WEB",
139+
"url": "https://github.com/angular/angular-cli/security/advisories/GHSA-x288-3778-4hhx"
140+
},
141+
{
142+
"type": "ADVISORY",
143+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27739"
144+
},
145+
{
146+
"type": "WEB",
147+
"url": "https://github.com/angular/angular-cli/pull/32516"
148+
},
149+
{
150+
"type": "WEB",
151+
"url": "https://angular.dev/best-practices/security#preventing-server-side-request-forgery-ssrf"
152+
},
153+
{
154+
"type": "WEB",
155+
"url": "https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF"
156+
},
157+
{
158+
"type": "PACKAGE",
159+
"url": "https://github.com/angular/angular-cli"
160+
}
161+
],
162+
"database_specific": {
163+
"cwe_ids": [
164+
"CWE-918"
165+
],
166+
"severity": "CRITICAL",
167+
"github_reviewed": true,
168+
"github_reviewed_at": "2026-02-25T22:42:36Z",
169+
"nvd_published_at": "2026-02-25T18:23:40Z"
170+
}
171+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-xh43-g2fq-wjrj",
4+
"modified": "2026-02-25T22:41:57Z",
5+
"published": "2026-02-25T22:41:57Z",
6+
"aliases": [
7+
"CVE-2026-27738"
8+
],
9+
"summary": "Angular SSR has an Open Redirect via X-Forwarded-Prefix",
10+
"details": "An Open Redirect vulnerability exists in the internal URL processing logic in Angular SSR. The logic normalizes URL segments by stripping leading slashes; however, it only removes a single leading slash.\n\nWhen an Angular SSR application is deployed behind a proxy that passes the `X-Forwarded-Prefix` header, an attacker can provide a value starting with three slashes (e.g., `///evil.com`).\n\n1. The application processes a redirect (e.g., from a router `redirectTo` or i18n locale switch).\n2. Angular receives `///evil.com` as the prefix.\n3. It strips one slash, leaving `//evil.com`.\n4. The resulting string is used in the `Location` header.\n5. Modern browsers interpret `//` as a protocol-relative URL, redirecting the user from `https://your-app.com` to `https://evil.com`.\n\n\n### Impact\nThis vulnerability allows attackers to conduct large-scale phishing and SEO hijacking:\n- **Scale:** A single request can poison a high-traffic route, impacting all users until the cache expires.\n- **SEO Poisoning:** Search engine crawlers may follow and index these malicious redirects, causing the legitimate site to be delisted or associated with malicious domains.\n- **Trust:** Because the initial URL belongs to the trusted domain, users and security tools are less likely to flag the redirect as malicious.\n\n### Attack Preconditions\n\n- The application must use Angular SSR.\n- The application must have routes that perform internal redirects.\n- The infrastructure (Reverse Proxy/CDN) must pass the `X-Forwarded-Prefix` header to the SSR process without sanitization.\n- The cache must not vary on the `X-Forwarded-Prefix` header.\n\n### Patches\n- 21.2.0-rc.1\n- 21.1.5\n- 20.3.17\n- 19.2.21\n\n### Workarounds\nUntil the patch is applied, developers should sanitize the `X-Forwarded-Prefix` header in their`server.ts` before the Angular engine processes the request:\n\n```ts\napp.use((req, res, next) => {\n const prefix = req.headers['x-forwarded-prefix']?.trim();\n if (prefix) {\n // Sanitize by removing all leading slashes\n req.headers['x-forwarded-prefix'] = prefix.replace(/^[/\\\\]+/, '/');\n }\n next();\n});\n\n```\n\n### Resources\n- [Report](https://github.com/angular/angular-cli/issues/32501)\n- [Fix](https://github.com/angular/angular-cli/pull/32521)",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "@angular/ssr"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "21.2.0-next.0"
29+
},
30+
{
31+
"fixed": "21.2.0-rc.1"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "< 21.2.0-rc.0"
38+
}
39+
},
40+
{
41+
"package": {
42+
"ecosystem": "npm",
43+
"name": "@angular/ssr"
44+
},
45+
"ranges": [
46+
{
47+
"type": "ECOSYSTEM",
48+
"events": [
49+
{
50+
"introduced": "21.0.0-next.0"
51+
},
52+
{
53+
"fixed": "21.1.5"
54+
}
55+
]
56+
}
57+
]
58+
},
59+
{
60+
"package": {
61+
"ecosystem": "npm",
62+
"name": "@angular/ssr"
63+
},
64+
"ranges": [
65+
{
66+
"type": "ECOSYSTEM",
67+
"events": [
68+
{
69+
"introduced": "20.0.0-next.0"
70+
},
71+
{
72+
"fixed": "20.3.17"
73+
}
74+
]
75+
}
76+
]
77+
},
78+
{
79+
"package": {
80+
"ecosystem": "npm",
81+
"name": "@angular/ssr"
82+
},
83+
"ranges": [
84+
{
85+
"type": "ECOSYSTEM",
86+
"events": [
87+
{
88+
"introduced": "19.0.0-next.0"
89+
},
90+
{
91+
"fixed": "19.2.21"
92+
}
93+
]
94+
}
95+
]
96+
}
97+
],
98+
"references": [
99+
{
100+
"type": "WEB",
101+
"url": "https://github.com/angular/angular-cli/security/advisories/GHSA-xh43-g2fq-wjrj"
102+
},
103+
{
104+
"type": "ADVISORY",
105+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27738"
106+
},
107+
{
108+
"type": "WEB",
109+
"url": "https://github.com/angular/angular-cli/issues/32501"
110+
},
111+
{
112+
"type": "WEB",
113+
"url": "https://github.com/angular/angular-cli/pull/32521"
114+
},
115+
{
116+
"type": "WEB",
117+
"url": "https://github.com/angular/angular-cli/commit/f086eccc36d10cf01c426e35864bc32e1e292323"
118+
},
119+
{
120+
"type": "PACKAGE",
121+
"url": "https://github.com/angular/angular-cli"
122+
}
123+
],
124+
"database_specific": {
125+
"cwe_ids": [
126+
"CWE-601"
127+
],
128+
"severity": "MODERATE",
129+
"github_reviewed": true,
130+
"github_reviewed_at": "2026-02-25T22:41:57Z",
131+
"nvd_published_at": "2026-02-25T17:25:40Z"
132+
}
133+
}

0 commit comments

Comments
 (0)