javascript / expert
Snippet
Bereinigung nutzerdefinierter Redirect-URLs vor der Angular-Router-Navigation
Redirect-Parameter (returnUrl, next, redirectTo) sind ein klassischer Open-Redirect-Vektor: Ein Angreifer baut einen Login-Link, dessen returnUrl auf eine externe Phishing-Domain zeigt, und navigiert die App nach der Authentifizierung blind dorthin, werden Nutzer stillschweigend weitergeleitet. Dieser Service erzwingt eine Präfix-Allowlist, weist protokollrelative URLs (//evil.com, die Browser als absolute URL desselben Schemas behandeln) sowie jede URL mit explizitem Schema ausdrücklich zurück und fällt auf einen sicheren Standardwert zurück, sobald die Eingabe die Validierung nicht besteht oder sich nicht einmal URI-dekodieren lässt.
snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Injectable, inject } from '@angular/core';import { Router, UrlTree } from '@angular/router';const ALLOWED_INTERNAL_PREFIX = '/app/';@Injectable({ providedIn: 'root' })class SafeRedirectService {private router = inject(Router);resolveSafeTarget(rawReturnUrl: string | null): UrlTree {if (!rawReturnUrl) {return this.router.parseUrl('/app/home');}let decoded: string;try {decoded = decodeURIComponent(rawReturnUrl);} catch {return this.router.parseUrl('/app/home');}const isProtocolRelative = decoded.startsWith('//');const isAbsoluteUrl = /^[a-z][a-z0-9+.-]*:/i.test(decoded);const isInternal = decoded.startsWith(ALLOWED_INTERNAL_PREFIX);if (isProtocolRelative || isAbsoluteUrl || !isInternal) {return this.router.parseUrl('/app/home');}return this.router.parseUrl(decoded);}}
angular
Erklärung
1
const ALLOWED_INTERNAL_PREFIX = '/app/';
Eine explizite Allowlist-Präfix ist deutlich sicherer als eine Denylist bekannter schlechter Muster, die Angreifer mit Encoding-Tricks umgehen können.
2
decoded = decodeURIComponent(rawReturnUrl);
Das Dekodieren vor der Validierung verhindert kodierte Umgehungen wie %2F%2Fevil.com, die eine naive String-Prüfung sonst passieren würden.
3
const isProtocolRelative = decoded.startsWith('//');
Protokollrelative URLs sind in Browsern trotz fehlendem Schema absolut und müssen daher separat vom Schema-Regex geprüft werden.
4
const isAbsoluteUrl = /^[a-z][a-z0-9+.-]*:/i.test(decoded);
Dieser Regex entspricht der RFC-3986-Schema-Grammatik und erfasst javascript:, https: und jede andere schemapräfixierte Payload.
5
return this.router.parseUrl('/app/home');
Jeder Ablehnungspfad mündet im selben sicheren UrlTree statt einer Exception, was die Navigationslogik des Aufrufers einfach hält.