~/blog/defending-a-static-spaPOSTED
von Shady Nathan Tawfik · 16. Mai 2026 · 4 min

Acht Angriffsvektoren, null Befunde: Wie eine gehärtete Single Page Application aussieht

Ein Negativbefund ist ein Ergebnis. Dieser dokumentiert, warum acht Standardvektoren gegen eine statisch ausgelieferte Anwendung hinter einer Edge-Plattform scheiterten.

Interaktiv[blog-2.0]

TL;DR: Ein Negativbefund ist ein Ergebnis. Ich habe acht Standard-Angriffsvektoren gegen eine statische Single Page Application gefahren, jeden Fehlschlag dokumentiert und die Liste in ein Regressions-Gate verwandelt, das bei jedem Build läuft.

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.

Eine Datenplattform als vorgerenderte Single Page Application auf einem statischen Host mit Edge-Runtime davor. Alle acht geplanten Vektoren wurden ausgeführt und dokumentiert, darunter Path Traversal, Server-Side Template Injection, API-Missbrauch, Storage-Freigabe und Autorisierungsumgehung.

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:

Die meisten veröffentlichten Web-Fundamente setzen einen Server voraus, der etwas rendert. Diese Annahme macht sie billig: hier injizieren, dort ausführen, Antwort lesen. Entfällt der serverseitige Renderer, hat die Mehrzahl dieser Techniken nichts, woran sie andocken könnte.

Ein statischer Build ändert die Form des Problems, statt es zu entfernen. Es gibt keine Laufzeit zum Injizieren, damit entfallen Server-Side Template Injection, Deserialisierung und die meisten Injektionsklassen vollständig. Die Angriffsfläche reduziert sich auf Edge-Konfiguration, Client-Bundle und die API, mit der das Bundle spricht.

Das Festhalten der Fehlschläge ist so wichtig wie das Festhalten der Erfolge. Ein Negativbefund ist der einzige Weg, eine Architektur zu verteidigen, die ein späterer Prüfer sonst infrage stellt, und die Vektorliste wird zur Regressionssuite.

TL;DR: Ein Negativbefund ist ein Ergebnis. Dieser dokumentiert, warum acht Standardvektoren gegen eine statisch ausgelieferte Anwendung hinter einer Edge-Plattform scheiterten.

Befunde

SchweregradBefundNachweis
INFOPath Traversal nicht erreichbarStatisches Routing liefert für jeden Pfad dasselbe Dokument
INFOServer-Side Template Injection unmöglichKein serverseitiges Rendering im Request-Pfad
INFOAdministrative Routen nicht ausgeliefertUnveröffentlichte Routen fehlen im Build-Output
INFOStorage-Buckets nicht öffentlichMedien werden über signierte, ablaufende URLs ausgeliefert
INFOAutorisierung in der Datenschicht durchgesetztDirekter Objektzugriff mit fremden Kennungen getestet
INFOEdge normalisiert Host und PfadHost-Header-Poisoning und Path-Confusion abgewiesen

Der Angriffspfad

rejectsAttack requestEdge platformStatic document storePre-rendered pageSigned API callAuthorisation in the data layerAuthorised data or refusalTraversal, injection, host tricks

Reproduktion im Labor

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

Die Regressionssuite gegen Ihre eigene statische Auslieferung laufen lassen

~/code/bash bash
# Ziel: Ihre eigene statische Seite auf localhost
$ python3 -m http.server 8080 --directory dist

# 1. Traversal und Verwirrung
$ for p in '/../etc/passwd' '/%2e%2e/%2e%2e/etc/passwd' '/index.html?x=../../secret'; do
    printf '%s -> %s\n' "$p" "$(curl -so /dev/null -w '%{http_code}' "http://localhost:8080$p")"
  done

# 2. unveröffentlichte Routen dürfen keinen Inhalt liefern
$ for p in /admin /api/admin /internal; do
    printf '%s -> %s\n' "$p" "$(curl -so /dev/null -w '%{http_code}' "http://localhost:8080$p")"
  done

Erkennung in eigener Infrastruktur

Veröffentlichte Routenliste mit dem Build-Output vergleichen

~/code/bash bash
# was der Build tatsächlich enthält
$ ls dist/assets | head

# und was die Edge für nicht enthaltene Pfade ausliefert
$ for p in /admin /api/admin /.env /config.json; do
    printf '%s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://example.org$p)"
  done

API-Autorisierung von aussen prüfen

~/code/bash bash
# ein Objekt verwenden, das Ihnen gehört, dann nur die Kennung tauschen
$ curl -s -o /dev/null -w '%{http_code}\n' 'https://api.example.org/v1/items/own-id'
$ curl -s -o /dev/null -w '%{http_code}\n' 'https://api.example.org/v1/items/other-id'

Behebung

Den Negativbefund als Regressions-Gate in der Pipeline halten

~/code/yaml yaml
# statische Oberflächenprüfung bei jedem Build ausführen
name: static-surface
on: [push, pull_request]
jobs:
  surface:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - name: unveröffentlichte Routen dürfen nicht auflösen
        run: |
          for p in /admin /api/admin /.env /config.json; do
            code=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:8080$p")
            test "$code" = "404" || { echo "geleakte Route: $p ($code)"; exit 1; }
          done

Fazit — was ich daraus mitnehme

  • Ein Negativbefund ist ein Ergebnis, nicht das Fehlen eines Ergebnisses.
  • Statische Auslieferung entfernt Injektionsklassen, nicht Autorisierung.
  • Protokollieren Sie die Vektoren, die nachweislich scheitern, und wiederholen Sie sie dauerhaft.
  • Bundle und API sind die gesamte Angriffsfläche, sobald der Server entfällt.

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