-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Description
Command
other
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
When using Angular server side rendering you can trick Angular in thinking the request is for a different domain.
Root cause
The room cause seems to be in the createRequestUrl function that glues together the protocol, host, port and quest path (url or originalUrl) using the native URL class constructor.
return new URL(originalUrl ?? url, `${protocol}://${hostnameWithPort}`); |
return new URL(originalUrl ?? url, `${protocol}://${hostnameWithPort}`);
When originalUrl or url looks like a schema relative url ('//something') it will ignore the second base argument. Zo in our example it would convert to return new URL('//something', 'http://localhost:4200') which according to the specs resolves to http://something
Attack scenario
As Angular thinks (using the PlatformLocator or DOCUMENT token location.href) it is handling a domain that can be defined in the URL path. An attacker can let the server execute requests to a server of their choosing if relative requests are used, possibly injecting content.
Executing a request to http://localhost:4200//domain-from-attacker.com/some-path will in the above example execute a request to http://domain-from-attacker.com
Minimal Reproduction
-
Create a new Angular SSR project using ng new --ssr and activate RenderMode.Server. See the example repo at https://github.com/meDavidNS/angular-ssr
-
For illustration purpose inject the DOCUMENT token and the httpClient to make a relative request.
protected readonly httpClient = inject(HttpClient)
protected readonly doc = inject(DOCUMENT)
protected readonly loc = inject(PlatformLocation)
ngOnInit() {
console.log({
'HOSTNAME': this.loc.hostname,
'LOCATION': this.doc.location.href,
})
this.httpClient.get('assets/some.json').subscribe(console.log)
}
- Request a page that starts the path with a double slash, like http://localhost:4200//something, this will trick Angular in thinking it is handling a request from http://something/ instead of http://localhost:4200. In the above example it will execute the http request to http://something/assets/some.json instead of http://localhost:4200/assets/some.json
Your Environment
Angular CLI: 20.3.5
Node: 22.14.0
Package Manager: npm 10.9.2
OS: darwin arm64
Angular: 20.3.4
... common, compiler, compiler-cli, core, forms
... platform-browser, platform-server, router
Package Version
------------------------------------
@angular-devkit/architect 0.2003.5
@angular-devkit/core 20.3.5
@angular-devkit/schematics 20.3.5
@angular/build 20.3.5
@angular/cli 20.3.5
@angular/ssr 20.3.5
@schematics/angular 20.3.5
rxjs 7.8.2
typescript 5.9.3
Anything else relevant?
No response