CSRF-Schutz
csrfProtection ist eine zustandslose CSRF-Abwehr: das Token ist
payload.hmac(secret, payload) und steckt in einem Cookie mit
__Host--Präfix. Sichere Methoden erzeugen/erneuern das Token; unsichere
Methoden (POST/PUT/PATCH/DELETE) müssen ein passendes Cookie und
einen passenden Header vorweisen.
import { csrfProtection, CsrfOptions, withMiddleware } from 'actor-ts/http';
const csrf = csrfProtection( CsrfOptions.create().withSecret(process.env.CSRF_SECRET!),);
const routes = withMiddleware(csrf, appRoutes);Bei einem sicheren Request wird das Token als (nicht-HttpOnly-)Cookie
gesetzt und zusätzlich an den Handler weitergereicht, der es mit
readCsrfToken(req) liest und in ein Formularfeld oder <meta>-Tag
einsetzt. Der Browser sendet es beim nächsten unsicheren Request im
X-CSRF-Token-Header (oder einem konfigurierten Formularfeld) zurück.
Was welcher Teil leistet
Abschnitt betitelt „Was welcher Teil leistet“Drei getrennte Dinge tragen die Abwehr, und es lohnt sich, genau zu sein, welches davon was leistet:
- Der HMAC weist Tokens ab, die dieser Server nie ausgestellt hat — Müll, geratene Werte, ein Token aus einem anderen Deployment. Er bindet ein Token an das Server-Secret und an sonst nichts: ein für einen Client ausgestelltes Token verifiziert also für jeden anderen. Er ist nicht das, was das Platzieren von Cookies verhindert.
- Der
__Host--Cookie-Name verhindert das Platzieren — und ist der Default. Ein Browser akzeptiert so ein Cookie nur mitSecure,Path=/und ohneDomain, was es exakt auf diesen Host festnagelt: eine Geschwister-Subdomain kann es nicht schreiben, eine Klartext-Origin überhaupt nicht. - Das Origin/Referer-Gate auf unsicheren Methoden weist den Cross-Site-Request ab, der ein platziertes Paar überhaupt erst mitbrächte.
Konfiguration
Abschnitt betitelt „Konfiguration“| Builder-Methode | Feld | Default |
|---|---|---|
withSecret(s) | secret | erforderlich, ≥ 16 Bytes |
withCookieName(n) | cookieName | '__Host-csrf-token' |
withHeaderName(n) | headerName | 'x-csrf-token' |
withCookie(attrs) | cookie | Path=/, Secure, SameSite=Lax |
withVerifyOrigin(flag?) | verifyOrigin | true — zusätzlich Origin/Referer prüfen |
withAllowedOrigins(...o) | allowedOrigins | zusätzlich akzeptierte vollständige Origins |
withExpectedScheme(s) | expectedScheme | 'https' — bzw. 'http', wenn das Cookie nicht Secure ist |
withFormField(name) | formFieldName | aus — das Token auch aus einem urlencoded Body-Feld lesen |
Was „same origin“ hier bedeutet
Abschnitt betitelt „Was „same origin“ hier bedeutet“Der Origin-Check vergleicht vollständige Origins — Scheme, Host und Port
— auf beiden Armen: die eigene Origin des Requests und jeden
allowedOrigins-Eintrag. https://app.example akzeptiert also weder
http://app.example noch https://app.example:8443, und ein Origin: null
(eine opake Origin, z. B. aus einem sandboxed iframe) wird immer abgewiesen.
Ein Request nennt seinen Host im Host-Header, aber niemals sein Scheme —
TLS kann am Proxy terminieren, und ein weitergereichter Scheme-Header wird
vom Client gesetzt, ist also nicht vertrauenswürdig. expectedScheme
liefert diese fehlende Hälfte. Default ist 'https'; csrfProtection liest
stattdessen 'http', wenn du das Secure-Cookie-Attribut ausdrücklich
abschaltest — denn damit ist ein Klartext-HTTP-Betrieb bereits erklärt:
const csrfOptions = CsrfOptions.create() .withSecret(process.env.CSRF_SECRET!) // Local dev over plain HTTP: a __Host- cookie cannot be set at all, and // dropping Secure also switches the expected scheme to http. .withCookieName('csrf-token') .withCookie({ secure: false });Einträge in allowedOrigins müssen vollständige Origins sein
('https://partner.example', nicht 'partner.example'); alles andere wirft
beim Bauen der Middleware einen OptionsError, weil es nie passen könnte.
Die leichtgewichtige Alternative — requireSameOrigin
Abschnitt betitelt „Die leichtgewichtige Alternative — requireSameOrigin“Brauchst du nur Schutz für moderne Browser, weist requireSameOrigin()
unsichere-Methoden-Requests ab, deren Origin/Referer nicht die eigene
Origin des Requests ist:
import { requireSameOrigin, withMiddleware } from 'actor-ts/http';
const routes = withMiddleware(requireSameOrigin(), appRoutes);Es nimmt dieselben allowedOrigins und expectedScheme (plus
allowMissingOrigin) mit demselben 'https'-Default — eine
Klartext-HTTP-Seite braucht
SameOriginOptions.create().withExpectedScheme('http'), denn hier gibt es
kein Cookie, aus dem sich das ableiten ließe.
csrfProtection ist die Doppel-Absicherung (es führt diesen Check ebenfalls
aus); requireSameOrigin allein ist leichter, verlässt sich aber darauf,
dass der Browser ein korrektes Origin/Referer sendet.
Wie weiter
Abschnitt betitelt „Wie weiter“- CORS — Umgang mit Cross-Origin-Requests.
- Sicherheit — wo CSRF im Stack sitzt.
