Skip to content

Comments

fix(@angular/ssr): prevent open redirect via X-Forwarded-Prefix header #32521

Merged
alan-agius4 merged 1 commit intoangular:mainfrom
alan-agius4:security-validate-main-x-prefix
Feb 23, 2026
Merged

fix(@angular/ssr): prevent open redirect via X-Forwarded-Prefix header #32521
alan-agius4 merged 1 commit intoangular:mainfrom
alan-agius4:security-validate-main-x-prefix

Conversation

@alan-agius4
Copy link
Collaborator

@alan-agius4 alan-agius4 commented Feb 20, 2026

This change addresses a security vulnerability where joinUrlParts() in packages/angular/ssr/src/utils/url.ts only stripped one leading slash from URL parts.

When the X-Forwarded-Prefix header contains multiple leading slashes (e.g., ///evil.com), the function previously produced a protocol-relative URL (e.g., //evil.com/home). If the application issues a redirect (e.g., via a generic redirect route), the browser interprets this 'Location' header as an external redirect to https://evil.com/home.

This vulnerability poses a risk as open redirects can be used in phishing attacks. Additionally, since the redirect response may lack Cache-Control headers, intermediate CDNs could cache the poisoned redirect, serving it to other users.

This commit fixes the issue by:

  1. Updating joinUrlParts to internally strip all leading and trailing slashes from URL segments, preventing the formation of protocol-relative URLs from malicious input.
  2. Adding strict validation for the X-Forwarded-Prefix header to immediately reject requests with values starting with multiple slashes.

Closes #32501

@alan-agius4 alan-agius4 force-pushed the security-validate-main-x-prefix branch from bdc4f23 to 33629cc Compare February 20, 2026 11:20
@alan-agius4 alan-agius4 force-pushed the security-validate-main-x-prefix branch from 33629cc to f181214 Compare February 20, 2026 13:50
@alan-agius4
Copy link
Collaborator Author

alan-agius4 commented Feb 20, 2026

@dgp1130 & @AndrewKushnir please ignore the first commit as this is from #32516.

@josephperrott, I added you by mistake as a reviewer, and I cannot remove you.

@alan-agius4 alan-agius4 added action: review The PR is still awaiting reviews from at least one requested reviewer labels Feb 20, 2026
@alan-agius4 alan-agius4 marked this pull request as ready for review February 20, 2026 15:59
@@ -95,26 +95,32 @@ export function addTrailingSlash(url: string): string {
* ```
*/
export function joinUrlParts(...parts: string[]): string {
Copy link
Collaborator

@dgp1130 dgp1130 Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: Could a regex be simpler? Something like:

const regex = new Regex('^/*(?<part>.*?)/*$');
const normalizedParts = parts.map((part) => regex.exec(part).groups['part']);
return addLeadingSlash(normalizedParts.join('/'));

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite a hot function, whilst I didn’t benchmark it I assume the above is much slower.

@alan-agius4 alan-agius4 force-pushed the security-validate-main-x-prefix branch from 0755920 to 66cb832 Compare February 23, 2026 09:10
@alan-agius4 alan-agius4 added target: patch This PR is targeted for the next patch release action: merge The PR is ready for merge by the caretaker target: rc This PR is targeted for the next release-candidate and removed action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release labels Feb 23, 2026
This change addresses a security vulnerability where `joinUrlParts()` in
`packages/angular/ssr/src/utils/url.ts` only stripped one leading slash from
URL parts.

When the `X-Forwarded-Prefix` header contains multiple leading slashes (e.g.,
`///evil.com`), the function previously produced a protocol-relative URL
(e.g., `//evil.com/home`). If the application issues a redirect (e.g., via
a generic redirect route), the browser interprets this 'Location' header
as an external redirect to `https://evil.com/home`.

This vulnerability poses a significant risk as open redirects can be used in
phishing attacks. Additionally, since the redirect response may lack
`Cache-Control` headers, intermediate CDNs could cache the poisoned redirect,
serving it to other users.

This commit fixes the issue by:
1. Updating `joinUrlParts` to internally strip *all* leading and trailing slashes
   from URL segments, preventing the formation of protocol-relative URLs from
   malicious input.
2. Adding strict validation for the `X-Forwarded-Prefix` header to immediately
   reject requests with values starting with multiple slashes (`//`) or backslashes (`\\`).

Closes angular#32501
@alan-agius4 alan-agius4 force-pushed the security-validate-main-x-prefix branch from 66cb832 to 877f017 Compare February 23, 2026 09:41
@alan-agius4 alan-agius4 removed the request for review from dgp1130 February 23, 2026 09:41
@alan-agius4 alan-agius4 merged commit f086ecc into angular:main Feb 23, 2026
10 checks passed
@alan-agius4 alan-agius4 deleted the security-validate-main-x-prefix branch February 23, 2026 09:42
@alan-agius4
Copy link
Collaborator Author

This PR was merged into the repository. The changes were merged into the following branches:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @angular/ssr target: rc This PR is targeted for the next release-candidate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Open Redirect via Unsanitized X-Forwarded-Prefix Header, Chainable to Web Cache Poisoning

4 participants