Cross-Site Request Forgery, kurz CSRF, gehört zu den ältesten und zugleich am häufigsten missverstandenen Angriffen auf Webanwendungen. Der Kern des Problems klingt harmlos: Ihr Browser schickt bei jeder Anfrage an eine Domain automatisch die dazu gespeicherten Cookies mit, auch dann, wenn die Anfrage gar nicht von Ihnen, sondern von einer fremden, bösartigen Seite ausgelöst wurde. Genau diese Automatik nutzt ein Angreifer aus, um in Ihrem Namen Aktionen auszuführen, während Sie eingeloggt sind.
Dieser Artikel erklärt CSRF von Grund auf: Wie der Angriff technisch abläuft, wann eine Anwendung überhaupt anfällig ist (und wann nicht), wie CSRF in die OWASP Top 10 einzuordnen ist, und wie Sie sich mit bewährten Mustern und konkretem Framework-Code aus Spring Security, Django und Node.js wirksam schützen. Außerdem grenzen wir CSRF sauber von zwei oft verwechselten Angriffen ab: Cross-Site Scripting (XSS) und Server-Side Request Forgery (SSRF).
Am Ende haben Sie nicht nur ein solides Verständnis des Angriffs, sondern auch eine praxistaugliche Checkliste und lauffähige Codebeispiele, die Sie direkt in Ihren Projekten einsetzen können.
Was ist Cross-Site Request Forgery?
Die OWASP Foundation definiert CSRF als einen Angriff, der einen angemeldeten Nutzer dazu zwingt, ungewollte Aktionen in einer Webanwendung auszuführen, bei der er gerade authentifiziert ist. Der Angreifer sieht die Serverantwort dabei nicht. CSRF zielt also nicht auf das Auslesen von Daten, sondern auf zustandsändernde Aktionen: Passwort ändern, E-Mail-Adresse überschreiben, Geld überweisen, einen neuen Administrator anlegen.
Der Angriff ist unter mehreren Namen bekannt. Sie finden in Fachtexten die Abkürzungen CSRF und XSRF, außerdem die anschaulichen Bezeichnungen "Session Riding" und "Sea Surf". Gemeint ist immer dasselbe: Der Angreifer reitet auf einer bestehenden, gültigen Sitzung des Opfers mit.
Formal ist CSRF als CWE-352 (Cross-Site Request Forgery) in der Common Weakness Enumeration von MITRE klassifiziert. MITRE beschreibt die Schwachstelle als "composite weakness": Die Anwendung kann nicht ausreichend überprüfen, ob eine Anfrage tatsächlich absichtlich vom sendenden Nutzer stammt oder von einem unautorisierten Akteur eingeschleust wurde. CWE-352 steht seit Jahren durchgehend in den CWE Top 25 der gefährlichsten Software-Schwächen.
Wie ein CSRF-Angriff Schritt für Schritt funktioniert
Der Ablauf beruht immer auf demselben Vertrauensmissbrauch:
- Sie melden sich bei einer Anwendung an, zum Beispiel Ihrem Online-Banking. Der Server setzt ein Session-Cookie in Ihrem Browser.
- Ohne sich abzumelden besuchen Sie in einem anderen Tab eine fremde Seite, die ein Angreifer kontrolliert (verlinkt per E-Mail, Chat oder Forenbeitrag).
- Diese Seite enthält versteckten Code, der eine Anfrage an die Bank-Domain auslöst.
- Ihr Browser hängt automatisch das gültige Session-Cookie der Bank an diese Anfrage an, weil sie an die Bank-Domain geht.
- Der Bank-Server sieht eine technisch korrekte, authentifizierte Anfrage und führt sie aus. Er kann sie nicht von einer echten, von Ihnen ausgelösten Anfrage unterscheiden.
GET-basierter Angriff
Wenn eine zustandsändernde Aktion über einen simplen GET-Request erreichbar ist, genügt bereits ein unsichtbares Bild. Der Browser lädt die "Bildquelle" und feuert damit die Anfrage ab:
<img src="http://bank.example/transfer.do?acct=MARIA&amount=100000" width="0" height="0" border="0">
Das Opfer sieht nur eine leere Stelle auf der Angreiferseite. Im Hintergrund wurde die Überweisung ausgelöst. Dass zustandsändernde Aktionen niemals über GET erreichbar sein dürfen, ist deshalb eine der wichtigsten Grundregeln.
POST-basierter Angriff
Die meisten sensiblen Aktionen laufen über POST. Auch das lässt sich fälschen, mit einem versteckten Formular, das per JavaScript automatisch abgeschickt wird:
<html>
<body>
<form action="https://bank.example/transfer" method="POST">
<input type="hidden" name="acct" value="MARIA" />
<input type="hidden" name="amount" value="100000" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
Sobald das Opfer die Seite öffnet, sendet der Browser das Formular ab, inklusive des mitgeschickten Session-Cookies. Es ist keinerlei Interaktion nötig.
Ein historisches, gegen die NVD verifiziertes Beispiel ist CVE-2008-6586 in der WebUI des BitTorrent-Clients µTorrent 0.315: Über CSRF ließ sich ohne jede Authentifizierung des Angreifers der Download beliebiger Torrent-Dateien erzwingen und sogar das Administrator-Konto verändern (CVSS 2.0: 6.8). Die Übernahme eines Administrator-Kontos ist dabei oft nur der erste Schritt, der bis zur vollständigen Übernahme des Servers eskalieren kann. Auch aktuellere Fälle existieren, etwa CVE-2024-12280 im WordPress-Plugin "WP Customer Area" (laut WPScan eine fehlende CSRF-Prüfung an einem Log-Löschen-Endpunkt), dieser Eintrag ist bislang allerdings nur über Sekundärquellen belegt.
Wann ist eine Anwendung überhaupt CSRF-anfällig?
Diese Frage ist entscheidend und wird in vielen Erklärungen übersprungen. MDN benennt drei Bedingungen, die gleichzeitig erfüllt sein müssen, damit klassisches CSRF überhaupt möglich ist:
- Die Anfrage ändert serverseitig einen Zustand (nicht nur ein reiner Lesezugriff).
- Die Anwendung identifiziert den Nutzer ausschließlich über Cookies, ohne zusätzlichen Nachweis wie einen Custom-Header oder ein Bearer-Token.
- Die Request-Parameter sind vollständig vorhersehbar, der Angreifer kann die Anfrage also im Voraus vollständig konstruieren.
Der zweite Punkt löst die häufigste Verwirrung bei API-Entwicklern auf: Reine Bearer-Token-APIs sind gegen klassisches CSRF grundsätzlich nicht anfällig. Wird das Zugriffs-Token im Authorization-Header übertragen (und nicht in einem Cookie), schickt der Browser es bei einer cross-site ausgelösten Anfrage nicht automatisch mit. Der Angreifer kann den Header nicht setzen, und die Anfrage bleibt unauthentifiziert.
CSRF ist damit im Kern ein Problem cookie- beziehungsweise session-basierter Authentifizierung. Sobald eine Anwendung sich nicht allein auf automatisch mitgeschickte Cookies verlässt, entfällt der Angriffsvektor. Das gilt allerdings nur, solange das Token wirklich ausschließlich im Header und nicht auch in einem Cookie liegt. Hybride Modelle, die Tokens in Cookies ablegen, holen sich das CSRF-Risiko zurück.
> Hinweis: CSRF ist nicht streng auf Cookie-Authentifizierung beschränkt. Auch HTTP-Basic- und Digest-Authentifizierung sind anfällig, weil der Browser die einmal eingegebenen Zugangsdaten bis zum Sitzungsende automatisch mitsendet.
CSRF in der OWASP Top 10: die editionsscharfe Historie
Rund um CSRF kursiert viel Halbwissen zur OWASP-Einordnung. Hier die belegte Chronologie:
- OWASP Top 10:2013 führte CSRF als eigenen Eintrag A8:2013 ("Cross-Site Request Forgery").
- OWASP Top 10:2017 entfernte CSRF als eigenständigen Punkt. Begründung: Viele Frameworks brachten inzwischen serienmäßigen CSRF-Schutz mit, und CSRF wurde nur noch in rund 5 % der getesteten Anwendungen gefunden. Gleichzeitig entstand 2017 die Kategorie "Broken Access Control".
- OWASP Top 10:2021 listet CSRF wieder explizit, allerdings nicht als eigenen Eintrag, sondern als "notable CWE" (CWE-352) innerhalb von A01: Broken Access Control, der Kategorie auf Platz 1.
- Auch die Ausgabe OWASP Top 10:2025 führt CWE-352 erneut ausdrücklich unter A01: Broken Access Control. Neu ist dort, dass zusätzlich Server-Side Request Forgery (CWE-918, SSRF) in dieselbe Kategorie A01 aufgenommen wurde.
Wichtig für die korrekte Einordnung: CSRF ist heute kein eigener Top-10-Eintrag mehr, sondern seit 2021 durchgehend Teil von A01 Broken Access Control. Wer behauptet, CSRF stehe weiterhin als eigener Punkt in den Top 10, arbeitet mit veraltetem Stand.
Die 2013/2017-Angaben stützen sich in unserer Recherche auf konsistente Sekundärquellen, die grundsätzliche Aussage (2013 eigener Eintrag, 2017 entfernt, seither Teil von Broken Access Control) ist in der Security-Community unstrittig. Die OWASP Top 10:2025 lag ab November 2025 als Release Candidate vor und wurde Anfang 2026 finalisiert; owasp.org/Top10/2025/ führt die Edition als finale Fassung, die CWE-352 erneut unter A01 Broken Access Control einordnet. Eine vertiefende Gesamtsicht bietet unser Artikel zur OWASP Top 10.
CSRF vs. XSS: verwandt, aber grundverschieden
CSRF und Cross-Site Scripting (XSS) werden oft in einem Atemzug genannt, weil beide "cross-site" im Namen tragen und in der CWE-Systematik als Nachbarn geführt werden (CWE-352 und CWE-79). Technisch sind es aber zwei sehr unterschiedliche Angriffe.
Die Kernabgrenzung: XSS schleust bösartigen Code direkt in eine Seite ein und führt ihn im Browser des Opfers aus. CSRF schleust keinen Code ein, sondern missbraucht eine bestehende Vertrauensbeziehung und die automatische Übermittlung von Zugangsdaten, um im Namen des Opfers eine Anfrage abzusetzen. Anders gesagt: XSS bricht die Vertrauensstellung des Browsers zur Seite, CSRF die Vertrauensstellung des Servers zum authentifizierten Nutzer.
Der praktisch wichtigste Zusammenhang: XSS hebelt jeden CSRF-Schutz aus. Wenn ein Angreifer über eine XSS-Lücke beliebiges JavaScript im Kontext der Zielseite ausführen kann, liest er das CSRF-Token einfach aus dem DOM oder Cookie aus und setzt eine perfekt gültige Anfrage ab. Die OWASP Cheat Sheet Series formuliert das unmissverständlich: "Cross-Site Scripting (XSS) vulnerabilities can bypass CSRF protections." Konsequente XSS-Prävention ist deshalb Voraussetzung für jeden funktionierenden CSRF-Schutz. Wie XSS im Detail funktioniert und wie Sie es verhindern, lesen Sie in unseren XSS-Grundlagen.
CSRF vs. SSRF: nur der Name ist ähnlich
Seit CSRF und SSRF in OWASP 2025 unter demselben Dach A01 stehen, werden sie noch häufiger verwechselt. In der Substanz haben sie fast nichts gemeinsam.
- CSRF (Cross-Site Request Forgery, CWE-352): Der Angreifer bringt den Browser eines Opfers dazu, eine Anfrage zu senden. Ausgenutzt wird die implizite Authentifizierung des Opfers (Session-Cookie). Angriffsrichtung: Client (Opfer-Browser) zum verwundbaren Server.
- SSRF (Server-Side Request Forgery, CWE-918): Der Angreifer bringt den Server dazu, eine Anfrage an eine von ihm gewählte Ressource zu senden, oft an ein internes System, das von außen gar nicht erreichbar wäre. Angriffsrichtung: verwundbarer Server zum internen Zielsystem.
Merksatz: Bei CSRF wird der Browser des Opfers als Werkzeug missbraucht, bei SSRF der Server selbst. Die gemeinsame OWASP-Kategorie ändert nichts an dieser grundlegend verschiedenen Angriffsmechanik.
Schutzmechanismen im Überblick
Es gibt nicht die eine Lösung, sondern eine gestaffelte Verteidigung. Die OWASP Cheat Sheet Series unterscheidet zwischen zwei primären Token-Mechanismen und mehreren ergänzenden "Defense-in-Depth"-Schichten.
Synchronizer Token Pattern (primär, für zustandsbehaftete Apps)
Das klassische und sicherste Muster: Der Server erzeugt pro Nutzer beziehungsweise Session ein kryptographisch unvorhersehbares Token, speichert es serverseitig und bettet es in jedes Formular ein (als verstecktes Feld oder als Custom-Header, niemals im Cookie allein). Bei jeder zustandsändernden Anfrage vergleicht der Server das mitgeschickte Token mit dem gespeicherten. Fehlt es oder passt es nicht, wird die Anfrage abgewiesen.
Der Angreifer kann dieses Token nicht erraten und wegen der Same-Origin-Policy auch nicht aus der Serverantwort auslesen. Wichtig: Tokens gehören nie in GET-URLs, weil sie dort über Browser-Historie, Logfiles, Referrer-Header und Netzwerkwerkzeuge leaken können.
Double-Submit-Cookie (primär, für zustandslose Apps und APIs)
Wenn der Server keinen serverseitigen Zustand halten soll, bietet sich das Double-Submit-Cookie an: Das Token wird sowohl als Cookie gesetzt als auch zusätzlich im Request-Parameter oder Header mitgeschickt. Der Server prüft, ob beide Werte übereinstimmen.
Hier ist eine wichtige Unterscheidung entscheidend:
- Naive Variante (nicht empfohlen): Ein zufälliger Cookie-Wert wird bloß gegen einen Request-Wert verglichen. Diese Variante ist anfällig für Cookie-Injection, besonders wenn ein Angreifer eine Subdomain oder das Netzwerkumfeld kontrolliert.
- Signierte Variante (empfohlen): Das Token wird per HMAC kryptographisch an die Session gebunden. Der zeitkonstante Vergleich schützt zusätzlich vor Timing-Angriffen.
Die OWASP Cheat Sheet Series beschreibt das signierte Double-Submit-Cookie als Verfahren so:
secret = getSecretSecurely("CSRF_SECRET")
sessionID = session.sessionID
randomValue = cryptographic.randomValue(64)
message = sessionID.length + "!" + sessionID + "!" + randomValue.length + "!" + randomValue.toHex()
hmac = hmac("SHA256", secret, message)
csrfToken = hmac.toHex() + "." + randomValue.toHex()
response.setCookie("csrf_token=" + csrfToken + "; Secure")
SameSite-Cookie-Attribut (Defense-in-Depth)
Das SameSite-Attribut steuert, ob ein Cookie bei Cross-Site-Anfragen überhaupt mitgeschickt wird. Es kennt drei Werte:
Strict: Das Cookie wird bei Cross-Site-Kontexten gar nicht gesendet. Empfohlen für hochsensible Sitzungs-Cookies, sofern die Nutzerführung es zulässt (Nachteil: folgt der Nutzer einem externen Link zur Seite, ist er zunächst scheinbar ausgeloggt).Lax: Das Cookie wird zusätzlich bei Top-Level-Navigation mit sicheren Methoden mitgeschickt. Dies ist der Browser-Standardwert.None: Kein Schutz, das Cookie geht in jedem Kontext mit. Erfordert zwingend dasSecure-Flag.
Ein wichtiger Fakt zum Standardverhalten: Setzt eine Anwendung kein SameSite-Attribut, behandeln moderne Browser das Cookie standardmäßig als Lax. Diese Umstellung führte Chrome mit Version 80 im Februar 2020 ein; andere Chromium-basierte Browser (Edge, Opera) folgten. Gelegentlich kursiert die Angabe "seit 2021", das bezieht sich bestenfalls auf die vollständige Durchsetzung, nicht auf die ursprüngliche Einführung.
So gesetzt wird ein Cookie mit dem Attribut ausgeliefert:
Set-Cookie: sessionid=7yjgj57e4n3d; SameSite=Strict; Secure; HttpOnly
Die Grenzen von SameSite müssen Sie kennen: SameSite=Lax schützt nicht, wenn eine zustandsändernde Aktion über einen GET-Request erreichbar ist. Außerdem bezieht sich SameSite auf die registrierbare Domain, nicht auf den Origin, wodurch kompromittierte Subdomains den Schutz umgehen können. SameSite ist deshalb eine wertvolle zusätzliche Schicht, aber kein alleiniger Ersatz für ein Token.
Origin- und Referer-Prüfung (Defense-in-Depth)
Der Server kann bei zustandsändernden Anfragen prüfen, ob der Origin-Header (oder ersatzweise der Referer) zur eigenen Domain passt. Der Origin-Header ist zu bevorzugen, weil er ausschließlich vom Browser gesetzt wird und seltener von Proxys oder Datenschutz-Tools entfernt wird als der Referer. Reine Referer-Prüfung gilt als schwächer und sollte nicht die einzige Verteidigung sein.
Custom-Header für JSON-APIs (Defense-in-Depth)
Bei AJAX-/JSON-Schnittstellen kann der Client einen beliebigen Custom-Header setzen (etwa X-CSRF-Token), den der Server verpflichtend verlangt. Das nutzt den CORS-Preflight-Mechanismus aus: Eine fremde Seite kann diesen Header cross-site nicht einfach setzen. Wichtig dabei: CORS darf niemals Access-Control-Allow-Origin: * mit Credentials kombinieren. Ein solches Muster wird von Browsern ohnehin verworfen; die reale Gefahr ist stattdessen die dynamische Rückspiegelung jeder anfragenden Origin zusammen mit Credentials.
Fetch-Metadata-Header (moderne Ergänzung)
Neuere Browser senden Sec-Fetch-Site, Sec-Fetch-Mode und verwandte Header, die nicht fälschbar sind. Der Server kann eine unsichere Methode aus einem cross-site-Kontext ablehnen. Die Browser-Unterstützung liegt bei über 95 %, ein Fallback auf klassische Header-Prüfung bleibt für ältere Clients nötig.
CSRF-Schutz in der Praxis: Framework-Code
Grau ist alle Theorie. Sehen wir uns an, wie führende Frameworks CSRF konkret abwehren. Alle Snippets stammen aus der offiziellen Framework-Dokumentation und sind lauffähig.
Spring Security (Java)
Wenn Sie unserer Spring-Boot-Reihe folgen, ist die gute Nachricht: Spring Security schützt standardmäßig gegen CSRF, ohne dass Sie eine Zeile Zusatzcode schreiben müssen. Für alle unsicheren Methoden (POST, PUT, DELETE) ist der Schutz aktiv. Explizit sieht die Konfiguration so aus:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(Customizer.withDefaults());
return http.build();
}
}
Standardmäßig speichert Spring das Token in der HttpSession (HttpSessionCsrfTokenRepository). Für JavaScript-Frontends (Single-Page-Apps) ist das Cookie-basierte Repository geeigneter, das ein XSRF-TOKEN-Cookie schreibt und den Header X-XSRF-TOKEN erwartet:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
}
Seit Spring Security 6 ist der XorCsrfTokenRequestAttributeHandler Standard. Er fügt dem übertragenen Token bei jeder Anfrage Zufälligkeit hinzu und erschwert damit BREACH-Angriffe (ein Kompressions-Seitenkanal). In einem klassischen Formular bettet Thymeleaf oder die Spring-Formular-Tag-Bibliothek das Token automatisch ein; manuell sieht das versteckte Feld so aus:
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
Für zustandslose REST-APIs, die ausschließlich per Bearer-Token authentifizieren, ist CSRF-Schutz überflüssig und darf bewusst deaktiviert werden. Empfohlen wird, den Schutz gezielt nur für die betroffenen Pfade auszunehmen, statt ihn pauschal global abzuschalten:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf((csrf) -> csrf
.ignoringRequestMatchers("/api/**")
);
return http.build();
}
}
Und weil Sicherheit ohne Tests wertlos ist: Spring Security liefert MockMvc-Erweiterungen, mit denen sich sowohl der Erfolgsfall (with(csrf())) als auch die korrekte Ablehnung eines ungültigen Tokens (with(csrf().useInvalidToken())) prüfen lassen.
@Test
public void loginWhenInvalidCsrfTokenThenForbidden() throws Exception {
this.mockMvc.perform(post("/login").with(csrf().useInvalidToken())
.accept(MediaType.TEXT_HTML)
.param("username", "user")
.param("password", "password"))
.andExpect(status().isForbidden());
}
Django (Python)
Django bringt CSRF-Schutz framework-weit über die CsrfViewMiddleware mit, die per Default aktiviert ist:
MIDDLEWARE = [
"django.middleware.csrf.CsrfViewMiddleware",
# ... weitere Middleware
]
In jedem internen POST-Formular fügen Sie das Template-Tag {% csrf_token %} ein. Django setzt daraufhin ein verstecktes Feld und prüft es serverseitig:
<form method="post">{% csrf_token %}
<!-- Formularfelder -->
</form>
Achtung: Verwenden Sie {% csrf_token %} niemals in Formularen, die an eine externe URL senden, sonst leaken Sie Ihr Token an Dritte.
Für AJAX-Aufrufe holen Sie das Token aus dem csrftoken-Cookie und senden es im Custom-Header X-CSRFToken. Der Parameter mode: 'same-origin' stellt sicher, dass das Token nicht an eine fremde Domain gerät:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + "=")) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie("csrftoken");
const request = new Request("/api/update", {
method: "POST",
headers: { "X-CSRFToken": csrftoken },
mode: "same-origin"
});
fetch(request).then((response) => {
// Antwort verarbeiten
});
Für einzelne Views bietet Django feingranulare Decorators: csrf_protect (Schutz erzwingen), csrf_exempt (Schutz bewusst abschalten), ensure_csrf_cookie (Cookie garantiert setzen, nützlich für reine AJAX-Seiten). Ein wichtiger Test-Hinweis: Der Django-Testclient entspannt CSRF-Prüfungen standardmäßig. Wollen Sie die Durchsetzung selbst testen, instanziieren Sie ihn explizit mit Client(enforce_csrf_checks=True).
Node.js / Express: Vorsicht bei csurf
Für Express kursiert in vielen älteren Tutorials das Paket csurf. Das ist nicht mehr aktuell: Das offizielle csurf-Repository (expressjs/csurf) ist archiviert und wird nicht mehr gepflegt. Es gibt keine Sicherheitsupdates und keine Bugfixes mehr. Ein Beispiel, wie csurf klassisch eingesetzt wurde (heute veraltet, nur zur Wiedererkennung):
// VERALTET: expressjs/csurf ist archiviert und wird nicht mehr gepflegt
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var express = require('express')
var csrfProtection = csrf({ cookie: true })
var app = express()
app.use(cookieParser())
app.get('/form', csrfProtection, function (req, res) {
res.render('send', { csrfToken: req.csrfToken() })
})
Aktuelle Empfehlung: Nutzen Sie eine gepflegte Bibliothek wie csrf-csrf (implementiert das Double-Submit-Cookie-Pattern für zustandslose Apps) oder csrf-sync (Synchronizer Token Pattern für session-basierte Apps). Prüfen Sie vor dem Einsatz jeweils den aktuellen Wartungsstand auf npm. Alternativ implementieren Sie den Schutz mit wenigen Zeilen selbst. Das folgende Beispiel zeigt das Double-Submit-Muster mit zeitkonstantem Vergleich:
const express = require('express');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
const app = express();
app.use(cookieParser());
app.use(express.urlencoded({ extended: false }));
// Token beim Ausliefern des Formulars setzen
app.get('/form', (req, res) => {
const token = crypto.randomBytes(32).toString('hex');
res.cookie('csrf_token', token, { sameSite: 'strict', secure: true });
res.send(`<form method="POST" action="/process">
<input type="hidden" name="_csrf" value="${token}">
<button type="submit">Absenden</button>
</form>`);
});
// Double-Submit-Prüfung als Middleware
function verifyCsrf(req, res, next) {
const cookieToken = req.cookies.csrf_token;
const bodyToken = req.body._csrf;
if (!cookieToken || !bodyToken ||
cookieToken.length !== bodyToken.length ||
!crypto.timingSafeEqual(Buffer.from(cookieToken), Buffer.from(bodyToken))) {
return res.status(403).send('CSRF-Validierung fehlgeschlagen');
}
next();
}
app.post('/process', verifyCsrf, (req, res) => {
res.send('Anfrage verarbeitet');
});
Für den Produktivbetrieb sollten Sie diese Basis um die HMAC-signierte Variante (siehe oben) oder eine geprüfte Bibliothek erweitern, da die naive Double-Submit-Prüfung allein anfällig für Cookie-Injection ist.
Weitere Frameworks in Kürze
- ASP.NET MVC: arbeitet mit dem Anti-Forgery-Token-Verfahren, einem Doppel-Token aus Cookie und verstecktem Formularfeld (
@Html.AntiForgeryToken()). Microsoft weist ausdrücklich darauf hin, dass SSL/HTTPS allein CSRF nicht verhindert, da auch die Angreiferseitehttps://-Anfragen senden kann. - Flask: über die Erweiterung Flask-WTF, aktiviert mit
CSRFProtect(app). - Angular: hat integrierten XSRF-Support über
withXsrfConfiguration, der dasXSRF-TOKEN-Cookie liest und alsX-XSRF-TOKEN-Header sendet. Passend dazu behandelt unser Artikel XSS-Schutz in React verwandte clientseitige Sicherheitsfragen.
Häufige Implementierungsfehler in der Praxis
Ein Token allein reicht nicht, wenn es falsch geprüft wird. Die PortSwigger Web Security Academy dokumentiert mehrere wiederkehrende Fehler, an denen echte Anwendungen scheitern:
- Token wird nur bei POST geprüft, nicht bei GET. Wenn dieselbe Aktion auch per GET erreichbar ist, umgeht der Angreifer die Prüfung.
- Fehlendes Token wird ignoriert statt abgelehnt. Manche Anwendungen prüfen das Token nur, wenn es vorhanden ist. Lässt der Angreifer es einfach weg, geht die Anfrage durch.
- Token ist nicht an die Session gebunden. Wird irgendein gültiges Token akzeptiert, kann der Angreifer sich selbst eines besorgen und es einsetzen.
- SameSite=Lax wird über einen zustandsändernden GET oder ein "On-Site-Gadget" umgangen.
- Referer-Prüfung lässt sich durch Weglassen oder Manipulieren des Headers austricksen.
Zusätzlich nennen Vendor-Quellen wie Wiz die klassische Fehlannahme, HTTPS allein schütze vor CSRF. Das ist falsch: TLS verschlüsselt die Übertragung, unterscheidet aber nicht zwischen einer echten und einer gefälschten Anfrage.
CSRF-Schutz-Cookies und Datenschutz
Für den deutschsprachigen Raum lohnt ein kurzer Blick auf die Datenschutzseite, der in fast allen technischen Erklärungen fehlt. Dies ist ausdrücklich keine Rechtsberatung, sondern eine Einordnung: Nach § 25 TDDDG (dem Nachfolger des TTDSG) benötigen Cookies grundsätzlich eine Einwilligung, es sei denn, sie sind für den Dienst "unbedingt erforderlich".
Ein CSRF-Cookie, das eine angemeldete Sitzung absichert, dürfte in vielen Fällen als technisch erforderlich gelten. Kritisch wird es, wenn ein CSRF-Cookie pauschal für alle Besucher gesetzt wird, auch für nicht angemeldete, bei denen gar keine schützenswerte Session existiert. In solchen Konstellationen ist die Einordnung als "erforderlich" fraglich. Da es funktional gleichwertige Alternativen ohne dauerhaftes Cookie gibt (Token als POST-Parameter, SameSite-Attribute, serverseitige Origin-Prüfung), lässt sich das Cookie oft auf die tatsächlich authentifizierten Kontexte begrenzen. Die konkrete rechtliche Bewertung sollten Sie im Zweifel fachkundig prüfen lassen.
Häufige Fragen zu CSRF
Was ist CSRF (Cross-Site Request Forgery)?
CSRF ist ein Angriff, der einen angemeldeten Nutzer dazu bringt, ungewollt eine zustandsändernde Aktion in einer Webanwendung auszuführen, bei der er gerade authentifiziert ist. Der Angreifer nutzt aus, dass der Browser gespeicherte Session-Cookies automatisch an die Zieldomain mitschickt, auch wenn die Anfrage von einer fremden Seite ausgelöst wurde. Die Serverantwort selbst sieht der Angreifer nicht, es geht ihm um die ausgelöste Aktion, etwa eine Überweisung oder eine Passwortänderung.
Was ist der Unterschied zwischen CSRF und XSS?
XSS (Cross-Site Scripting) schleust bösartigen Code in eine Seite ein und führt ihn im Browser des Opfers aus. CSRF schleust keinen Code ein, sondern missbraucht eine bestehende Vertrauensbeziehung, um im Namen des Opfers eine Anfrage abzusetzen. Wichtig: XSS hebelt jeden CSRF-Schutz aus, weil ein Angreifer über eine XSS-Lücke das CSRF-Token direkt aus dem DOM oder Cookie auslesen kann. Konsequente XSS-Prävention ist deshalb Voraussetzung für funktionierenden CSRF-Schutz.
Wie schützt man sich vor CSRF?
Der wirksamste Schutz ist ein CSRF-Token: Bei zustandsbehafteten Anwendungen das Synchronizer Token Pattern, bei zustandslosen Apps und APIs das signierte Double-Submit-Cookie oder ein verpflichtender Custom-Header. Ergänzend wirken das SameSite-Cookie-Attribut und eine serverseitige Origin-Prüfung als zusätzliche Schichten. Zustandsändernde Aktionen dürfen zudem niemals über GET erreichbar sein. Moderne Frameworks wie Spring Security oder Django bringen diesen Schutz standardmäßig mit.
Schützt das SameSite-Cookie-Attribut allein vor CSRF?
Nein. SameSite (insbesondere Strict) ist eine wertvolle Defense-in-Depth-Schicht, aber kein vollständiger Ersatz für ein Token. SameSite=Lax, der Browser-Standard, schützt nicht, wenn eine zustandsändernde Aktion über einen GET-Request erreichbar ist. Außerdem bezieht sich SameSite auf die registrierbare Domain und nicht auf den Origin, wodurch eine kompromittierte Subdomain den Schutz umgehen kann. SameSite gehört deshalb immer mit einem Token kombiniert.
Sind REST-APIs mit Bearer-Token CSRF-anfällig?
Reine Bearer-Token-APIs sind gegen klassisches CSRF grundsätzlich nicht anfällig. Wird das Zugriffs-Token im Authorization-Header übertragen und nicht in einem Cookie, schickt der Browser es bei einer cross-site ausgelösten Anfrage nicht automatisch mit, und der Angreifer kann den Header nicht setzen. Das gilt allerdings nur, solange das Token ausschließlich im Header liegt. Hybride Modelle, die Tokens in Cookies ablegen, holen sich das CSRF-Risiko zurück.
Fazit und Checkliste
CSRF ist kein exotischer Randfall, sondern ein struktureller Angriff auf die Grundmechanik cookie-basierter Sitzungen. Die gute Nachricht: Moderne Frameworks bringen wirksamen Schutz mit, oft schon standardmäßig. Die schlechte: Ein einziger Konfigurationsfehler, ein zustandsändernder GET-Endpunkt oder eine offene XSS-Lücke genügen, um den Schutz auszuhebeln.
Ihre Checkliste für die Praxis:
- Zustandsändernde Aktionen niemals über GET erreichbar machen.
- Für zustandsbehaftete Anwendungen das Synchronizer Token Pattern einsetzen (Framework-Schutz aktiviert lassen).
- Für zustandslose Apps und APIs das signierte Double-Submit-Cookie oder einen verpflichtenden Custom-Header verwenden.
- SameSite (bevorzugt
Strictfür sensible Sitzungs-Cookies) und Origin-Prüfung als zusätzliche Schichten setzen. - Reine Bearer-Token-APIs sind nicht klassisch CSRF-anfällig, hier CSRF-Schutz bewusst und gezielt deaktivieren.
- XSS konsequent verhindern, weil XSS jeden CSRF-Schutz aushebelt.
- Keine veralteten Bibliotheken verwenden:
csurfist archiviert, auf gepflegte Alternativen umsteigen. - CSRF korrekt einordnen: seit 2021 Teil von A01 Broken Access Control in der OWASP Top 10, kein eigener Eintrag mehr.
Wer diese Punkte beherzigt, macht klassisches CSRF praktisch chancenlos.