~/blog/spa-route-enumeration-angularPOSTED
von Shady Nathan Tawfik · 23. Mai 2026 · 3 min

Eine Single Page Application liefert ihre Routenkarte selbst aus

Eine moderne Framework-Anwendung mit fast keiner serverseitigen Angriffsfläche legte ihre Verwaltungsoberfläche trotzdem offen, weil die Routentabelle in das Client-Bundle kompiliert wird.

Interaktiv[blog-2.0]

TL;DR: Eine Single Page Application liefert ihre Routenkarte selbst aus: Routentabelle, Guard-Namen und Umgebungskonfiguration liegen im Client-Bundle. Ich habe alles mit einem einzigen Skript-Download extrahiert.

Ausgangslage

Ich habe das Ziel als Black-Box geprüft: keine Zugangsdaten, nur die öffentlich sichtbare Fläche. Das ist, was ich sah, bevor ich etwas angefasst habe.

Ein Portal einer Industrie- und Handelskammer auf Basis eines modernen Komponentenframeworks, ausgeliefert als vorgerenderte Anwendung über ein Content Delivery Network. Im Fokus stand, was die ausgelieferten Client-Assets über Routen, Umgebungen und Verwaltungsfunktionen preisgeben.

Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird ausschließlich nach Branche und Technologieklasse beschrieben, damit das Muster übertragbar bleibt.

Das Muster

Das Muster, das ich erkannt habe — und das ich in ähnlichen Landschaften immer wieder finde:

Prerendering hilft Suchmaschinen und tut nichts für Vertraulichkeit. Der Router, der entscheidet, was ein Nutzer erreichen darf, ist ein Client-Modul. Damit liefern die vollständige Routentabelle, die lazy geladenen Feature-Chunks und die Umgebungskonfiguration an jeden Besucher.

Das dreht die Ökonomie der Aufklärung um. Wo ein klassisches Ziel ein Verzeichnis-Scan kostet, liefert ein clientseitig gerendertes Ziel dieselbe Information aus einem einzigen Skript-Download. Relevant ist nicht die Größe, sondern die Lesbarkeit: Pfadstrings, Berechtigungs-Guards, Rollennamen und API-Basis-URLs überleben die Minification.

TL;DR: Eine moderne Framework-Anwendung mit fast keiner serverseitigen Angriffsfläche legte ihre Verwaltungsoberfläche trotzdem offen, weil die Routentabelle in das Client-Bundle kompiliert wird.

Befunde

SchweregradBefundNachweis
MITTELVollständige Routentabelle im Client-Bundle lesbarAdministrative und interne Ansichten aus Assets enumeriert
MITTELNamen der Berechtigungs-Guards preisgegebenDie vorgesehene Autorisierungslogik im Bundle beschrieben
MITTELUmgebungskonfiguration ins Bundle kompiliertAPI-Basisadressen pro Umgebung
NIEDRIGSource Map öffentlich ausgeliefertOriginalquellen rekonstruierbar
NIEDRIGLazy Chunks über vorhersagbare Namen enumerierbarFeature-Module einzeln ladbar
INFOServerseitige Fläche minimalKein serverseitiges Rendering und keine Datenbank erreichbar

Der Angriffspfad

Download one script fileExtract route path stringsEnumerate administrative screensRead permission guard namesLearn the intended authorisation ruleProbe the API behind those routes

Reproduktion im Labor

Jeder Befehl unten zielt auf einen Lab-Container, den ich selbst kontrolliere. Nichts davon ist auf ein laufendes System gerichtet.

Die Routenkarte aus dem eigenen Bundle lesen

~/code/bash bash
# Ziel: Ihre eigenen Build-Assets
$ curl -s https://example.org/ | grep -oE '/assets/[A-Za-z0-9._-]+\.js' | sort -u | head

$ for f in $(curl -s https://example.org/ | grep -oE '/assets/[A-Za-z0-9._-]+\.js' | sort -u); do
    curl -s "https://example.org$f"
  done > /tmp/bundle.txt

# routenähnliche Strings überleben die Minification
$ grep -oE '"/[a-z0-9/-]{4,40}"' /tmp/bundle.txt | sort -u | head -40

# Guard- und Rollenkennungen
$ grep -oiE '(canActivate|hasRole|isAdmin|requiresAuth|permission)[A-Za-z]*' /tmp/bundle.txt | sort -u | head

Erkennung in eigener Infrastruktur

Prüfen, ob Source Maps und Umgebungsdateien ausgeliefert werden

~/code/bash bash
$ curl -so /dev/null -w '%{http_code}\n' https://example.org/assets/main.js.map
$ curl -so /dev/null -w '%{http_code}\n' https://example.org/environments/environment.prod.js
$ curl -so /dev/null -w '%{http_code}\n' https://example.org/assets/config.json

Auf Legacy-CMS-Routen auf demselben Host achten

~/code/bash bash
$ for p in /wp-json/ /xmlrpc.php /api/graphql /.well-known/security.txt; do
    printf '%s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://example.org$p)"
  done

Behebung

Source Maps nicht in Produktion ausliefern

~/code/json json
{
  "production": {
    "sourceMap": false,
    "namedChunks": false
  }
}

Umgebungswerte auf dem Server halten, nicht im Bundle

~/code/ts ts
// das Bundle darf keine umgebungspezifischen Endpunkte enthalten
// nur injizieren, was der Browser wirklich braucht, und nichts, das Ihre Landschaft kartiert
export const environment = {
  production: true,
  apiBase: '/api',            // relativer Same-Origin-Pfad, niemals ein vollständiger Host
};

Autorisierung auf dem Server durchsetzen, den Guard nur als UX behandeln

~/code/ts ts
// der Client-Guard verbessert die Erfahrung, der Server entscheidet
// jede administrative Route braucht dieselbe serverseitige Sitzungsprüfung
export const canActivate = () => {
  const session = inject(SessionService);
  return session.hasRole('admin') ? true : inject(Router).parseUrl('/login');
};

Sobald ein Angreifer Zugriff hat, kann er folgende Aktionen durchführen:

Vollständige Routentabelle im Client-Bundle lesbar
Namen der Berechtigungs-Guards preisgegeben
Umgebungskonfiguration ins Bundle kompiliert

Fazit — was ich daraus mitnehme

  • Clientseitige Routing-Guards sind Benutzererlebnis, niemals Autorisierung.
  • Eine Routentabelle im Bundle ist eine Angriffsflächenkarte ohne Ablaufdatum.
  • Source Maps in Produktion abschalten und aufhören, Besuchern Ihre Landschaft zu beschreiben.
  • Achten Sie auf ein Legacy-CMS auf demselben Host.

Nur zu Bildungszwecken. Alle reproduzierbaren Befehle zielen auf meine eigene Lab-Umgebung (localhost), nie auf ein laufendes System. © 2026