CSRF (Cross-Site Request Forgery)
Cross-Site Request Forgery (CSRF) ist ein Angriff bei dem ein Angreifer den Browser eines angemeldeten Nutzers dazu bringt, unbeabsichtigte HTTP-Anfragen an eine Webanwendung zu senden - im Namen des Nutzers, ohne sein Wissen. Zählt zu den OWASP Top 10 und ist besonders gefährlich bei state-changing Aktionen.
CSRF-Angriffe nutzen das Vertrauen aus, das Browser-Sessions aufgebaut haben: Cookies werden automatisch mitgesendet - auch wenn der Request von einer fremden Website kommt. Ein einziger Klick auf einen präparierten Link kann Kontoeinstellungen ändern, Transaktionen auslösen oder Accounts übernehmen.
Wie CSRF funktioniert
Das Angriffsszenario einer Geldüberweisung verläuft in sechs Schritten:
- Das Opfer ist bei
bank.deeingeloggt (Session-Cookie vorhanden). - Der Angreifer erstellt eine präparierte Webseite unter
evil.com/malicious.html. - Das Opfer klickt auf einen Link zu
evil.com, z.B. über eine Phishing-Mail. evil.comlädt im Browser des Opfers entweder ein verstecktes Bild-Tag (<img src="https://bank.de/transfer?amount=1000&to=attacker" />) oder ein automatisch abgesendetes verstecktes Formular.- Der Browser sendet den Request zu
bank.de- mit dem Session-Cookie des Opfers, automatisch und ohne Wissen des Opfers. bank.dehält den Request für einen legitimen Nutzeraufruf und führt die Überweisung aus.
Bedingungen für CSRF
- Das Opfer muss eingeloggt sein (Session-Cookie gültig)
- Die Anwendung prüft nicht, ob der Request von der eigenen Seite stammt
- Der Angreifer kennt die Ziel-URL (oft leicht zu erraten)
- Keine CSRF-Schutzmaßnahmen sind implementiert
CSRF vs. XSS - Wichtiger Unterschied
| Eigenschaft | XSS | CSRF |
|---|---|---|
| Angriffsrichtung | Angreifer injiziert JavaScript in die Ziel-Website | Angreifer täuscht Browser, Request zu senden |
| Kontrolle | Angreifer kontrolliert Code im Kontext der Ziel-Site | Angreifer sieht die Response nicht (Same-Origin Policy) |
| Gefährliche Aktionen | Cookies stehlen, Formulardaten abgreifen | Nur state-changing Aktionen (POST/PUT/DELETE) |
| CSRF-Schutz | Umgeht CSRF-Schutz (Code im selben Origin) | GET-Requests die Daten ändern sind besonders gefährdet |
Merkhilfe: XSS lässt Angreifer Browser-Daten lesen und manipulieren. CSRF täuscht den Browser, Aktionen im Namen des Nutzers auszuführen.
CSRF-Schutzmaßnahmen
1. CSRF-Tokens (Synchronizer Token Pattern) - Hauptmaßnahme
Der Server generiert einen zufälligen Token, speichert ihn in der Session und bettet ihn in jedes Formular ein. Bei jedem State-Changing-Request prüft der Server, ob der mitgesendete Token mit dem Session-Token übereinstimmt.
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="a9d3e7b2c4f1..." />
<input name="amount" />
<button>Überweisen</button>
</form>
// Express.js mit csurf:
import csrf from 'csurf';
const csrfProtection = csrf({ cookie: false }); // Session-basiert!
app.get('/transfer', csrfProtection, (req, res) => {
res.render('transfer', { csrfToken: req.csrfToken() });
});
app.post('/transfer', csrfProtection, (req, res) => {
// csurf prüft Token automatisch!
// Wenn Token fehlt/falsch → 403 Forbidden
});
2. SameSite Cookie Attribute - Moderne Hauptmaßnahme
Das SameSite-Attribut steuert, wann Cookies bei Cross-Site-Requests mitgesendet werden:
| Wert | Verhalten | Einsatz |
|---|---|---|
Strict | Cookie wird nie bei Cross-Site-Requests gesendet - absoluter CSRF-Schutz | Problematisch: Nutzer der über externen Link kommt, ist nicht eingeloggt |
Lax | Cookie nur bei Top-Level-Navigation mit GET | Empfehlung für die meisten Apps; schützt vor CSRF-Formularen (POST), erlaubt normale externe Links |
None; Secure | Cookie bei allen Cross-Site-Requests - nur mit HTTPS | Nur für CORS-APIs die Cookies brauchen (z.B. Widgets) |
Set-Cookie: session=abc; SameSite=Lax; Secure; HttpOnly; Path=/
Hinweis: Seit Chrome 80 ist
SameSite=Laxder Default, wenn kein SameSite-Attribut gesetzt ist.
3. Double Submit Cookie Pattern
Als Alternative wenn serverseitige Sessions nicht verfügbar sind:
- Server setzt CSRF-Token als Cookie (nicht httpOnly!)
- JavaScript liest den Cookie und sendet den Token im Request-Header
- Server prüft: Cookie-Token == Header-Token?
Vorteil: Funktioniert ohne serverseitige Session. Nachteil: Anfällig für Subdomain-Angriffe (XSS auf subdomain.firma.de kann den Cookie lesen und manipulieren).
4. Custom Request Header (für AJAX/APIs)
APIs die ausschließlich JSON akzeptieren, können folgende Header-Checks nutzen:
request.headers['X-Requested-With'] == 'XMLHttpRequest'request.headers['Content-Type'] == 'application/json'
Normale Browser-Formulare können keine Custom-Headers senden - dieser Check funktioniert daher als impliziter CSRF-Schutz. Achtung: Nur für APIs verwenden, nicht für HTML-Formulare.
Gefährdete HTTP-Methoden
GET-Requests sollten niemals state-changing sein (REST-Prinzip). Sie sind besonders gefährdet, weil Browser, Crawler und Proxies GETs cachen und automatisch ausführen. Ein Beispiel für eine kritische Schwachstelle: <img src="https://example.com/delete-account">.
POST-, PUT- und DELETE-Requests sind das Hauptziel von CSRF und müssen mit Token oder SameSite geschützt werden.
Ausnahmen (kein CSRF-Schutz nötig):
- Rein lesende GET-Requests ohne State Changes
- Endpoints die nur via API genutzt werden (kein Browser-Cookie)
- OAuth2-Token-Endpoints (kein Cookie) - aber:
state-Parameter als CSRF-Schutz implementieren
CSRF in modernen SPAs
Single Page Applications (React, Vue, Angular) senden API-Requests per fetch oder axios, nicht über HTML-Formulare. Da Custom Headers gesetzt werden können, besteht ein natürlicher CSRF-Schutz für API-Calls. Cookies müssen jedoch weiterhin geschützt werden.
Modernes Setup für SPAs
Backend (Express) mit JWT im Authorization-Header:
// JWT im Authorization-Header: KEIN CSRF-Problem!
// Cross-Site-Formulare können keinen Authorization-Header senden.
// Session/Cookie-Auth: SameSite=Lax reicht meist:
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax'
});
Frontend (React) mit JWT:
// Authorization Header (JWT): kein Cookie → kein CSRF
fetch('/api/transfer', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount: 100 }),
});
// Cookie-Auth mit CSRF-Token:
fetch('/api/transfer', {
method: 'POST',
credentials: 'include', // Cookie mitsenden
headers: {
'X-CSRF-Token': csrfToken, // Token aus Meta-Tag oder API
'Content-Type': 'application/json',
},
});
CSRF in Penetrationstests erkennen
Manuelle CSRF-Tests
-
State-changing Endpoints identifizieren: Alle POST/PUT/DELETE-Requests mit Burp Suite protokollieren, CSRF-Token entfernen und prüfen ob der Request trotzdem erfolgreich ist.
-
CSRF-Token-Qualität prüfen:
- Ist der Token zufällig oder vorhersehbar?
- Gilt der Token pro Session oder pro Request?
- Ist der Token in der URL enthalten? (URL wird geloggt → Token-Leakage)
- Ist der Token zu kurz? (weniger als 128 Bit Entropie)
-
SameSite-Cookie prüfen:
curl -v https://example.com/loginund Set-Cookie Header auf SameSite prüfen. -
CORS-Headers prüfen:
Access-Control-Allow-Origin: *kombiniert mitAccess-Control-Allow-Credentials: trueist eine CORS-Misconfiguration. -
Referer-Header-Check: Requests ohne Referer-Header senden und prüfen ob akzeptiert. Dies ist eine schwache Maßnahme, da der Referer gefälscht werden kann.
Automatisierte Tools
- Burp Suite: CSRF PoC Generator
- OWASP ZAP: CSRF-Scanner
- csrf-poc-generator (npm): automatische PoC-Erstellung
Beispiel: CSRF-PoC für Reporting
<!-- CSRF Proof-of-Concept (für Pentest-Reports) -->
<!DOCTYPE html>
<html>
<head><title>CSRF PoC</title></head>
<body>
<h1>CSRF Test - Automatischer POST nach bank.de</h1>
<form id="csrf-form"
action="https://bank.de/api/transfer"
method="POST">
<input type="hidden" name="amount" value="1000" />
<input type="hidden" name="recipient" value="IBAN_ANGREIFER" />
<input type="hidden" name="note" value="CSRF-Test" />
</form>
<script>
// Automatisches Absenden ohne Nutzerinteraktion
document.getElementById('csrf-form').submit();
</script>
</body>
</html>
CVSS-Bewertung (Beispiel: CSRF auf Kontoüberweisung)
| Metrik | Wert |
|---|---|
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | Required |
| Scope | Unchanged |
| Confidentiality | None |
| Integrity | High |
| Availability | None |
| CVSS Base Score | 6.5 (Medium) |
Die tatsächliche Einstufung ist hoch, wenn state-changing Aktionen betroffen sind wie Passwortänderungen, Admin-Rechtevergabe oder Datenlöschung.