Cross-Site Request Forgery ist ein Klassiker der Web-Sicherheit — und gleichzeitig die Schwachstellen-Klasse, die durch Browser-Änderungen der letzten Jahre am stärksten zurückgegangen ist. Seit Cookies standardmäßig SameSite=Lax sind (Chrome 2020, Firefox/Edge nachgezogen), funktionieren die meisten klassischen CSRF-Angriffe nicht mehr ohne weiteres. Trotzdem bleibt CSRF in OAuth-Flows, in SPA-Endpunkten und in spezifischen Edge-Cases relevant. Dieser Artikel ordnet das Konzept, die Vektoren und legt die Karte für die folgenden Schutz-Artikel.

Was CSRF ist

CSRF nutzt eine spezifische Browser-Eigenschaft aus: Cookies werden automatisch mit Cross-Site-Requests mitgesendet (oder wurden es zumindest historisch).

Stell dir vor, du bist bei deiner Bank bank.example eingeloggt. Dein Browser hat ein Session-Cookie. Du öffnest in einem anderen Tab eine Angreifer-Seite evil.example. Diese Seite enthält versteckt:

HTML csrf-classic.html
<!-- Auf evil.example -->
<form action="https://bank.example/transfer" method="POST" id="csrf">
  <input name="to" value="attacker-account">
  <input name="amount" value="10000">
</form>
<script>document.getElementById('csrf').submit();</script>

Wenn du evil.example aufrufst, submittet das Formular automatisch eine POST-Anfrage an die Bank. Dein Browser sendet dabei dein Bank-Session-Cookie mit — die Bank sieht eine authentifizierte Anfrage und führt die Überweisung aus. Ohne dein Wissen, ohne dein aktives Zutun.

Das ist CSRF: der Angreifer nutzt deine Authentifizierung aus, ohne sie zu kennen.

Wichtige Unterschiede zu XSS:

  • CSRF läuft auf einer fremden Origin (evil.example) und schickt eine Anfrage an die Ziel-Origin (bank.example).
  • XSS läuft innerhalb der Ziel-Origin — der schadhafter Code wird auf bank.example selbst ausgeführt.

CSRF ist „ich kenne dein Passwort nicht, aber ich nutze deinen Browser". XSS ist „ich bin in deiner Origin und sehe alles".

Voraussetzungen für CSRF

CSRF funktioniert nur unter bestimmten Bedingungen:

  • Opfer ist eingeloggt in der Ziel-Anwendung — sonst gibt es kein Session-Cookie, das missbraucht werden kann.
  • Authentifizierung läuft über Cookies (oder ähnliche Browser-State-Mechanik) — Bearer-Token-Header werden nicht automatisch mitgesendet.
  • Aktion ist über eine simple HTTP-Anfrage auslösbar — typisch POST mit Form-Daten oder GET mit Side-Effects.
  • Anwendung prüft nicht, ob die Anfrage von der eigenen Origin kommt.

Wenn auch nur eine dieser Bedingungen nicht erfüllt ist, ist CSRF strukturell schwer:

  • Nicht eingeloggt → kein State zu missbrauchen.
  • Bearer-Token (JWT in Header) → kein automatisches Mitsenden.
  • Action verlangt JavaScript-State (Custom-Header, Cookies-from-Different-Domain) → automatisches Mitsenden klappt nicht.
  • Origin-Check oder CSRF-Token → Angreifer-Anfrage scheitert.

Genau diese Bedingungen sind in den letzten Jahren durch SameSite-Cookies und durch den Trend zu Bearer-Token-Auth in SPAs deutlich gebrochen worden.

Klassische CSRF-Vektoren

Historisch funktionierten viele unterschiedliche Wege, eine fremde Anfrage auszulösen:

Auto-Submitting Form — wie oben gezeigt; HTML-Formular per JavaScript abgeschickt.

Bild-Tag mit GET-Action:

HTML csrf-img.html
<!-- GET-Endpoint mit Side-Effect — schlechtes Design -->
<img src="https://bank.example/transfer?to=attacker&amount=10000">

Browser lädt das „Bild" — schickt dabei den Request mit Cookies. Wenn der Bank-Endpunkt eine GET-Aktion mit Wirkung hat, klappt CSRF ohne Form.

iframe mit Form-Submit — versteckt im DOM, ohne Sicht:

HTML csrf-iframe.html
<iframe style="display:none" name="hidden"></iframe>
<form action="https://bank.example/transfer" method="POST" target="hidden">
  <input name="to" value="attacker">
</form>
<script>document.forms[0].submit();</script>

JSON-Request via XMLHttpRequest — funktioniert nur, wenn CORS es erlaubt; klassisch durch SOP blockiert.

WebSocket-Connection — selten als CSRF-Vektor genutzt, aber Cookies werden mitgesendet.

Die meisten Vektoren haben gemeinsam: kein User-Aktiv-Klick auf der Ziel-Site, aber automatisch mitgesendete Cookies.

Was sich seit 2020 geändert hat

Die wichtigste strukturelle Änderung: SameSite-Cookies als Browser-Default.

Seit Chrome 80 (2020), Firefox 96 (2022), Edge 80 (2020), Safari 13 (2019, mit Eigenheiten): Cookies ohne explizites SameSite-Attribut werden als SameSite=Lax behandelt.

Was das bedeutet:

  • SameSite=Lax — Cookie wird bei Cross-Site-Anfragen nicht mitgesendet, außer bei Top-Level-Navigation per GET (z. B. Link-Klick).
  • SameSite=Strict — Cookie wird bei gar keinem Cross-Site-Request mitgesendet.
  • SameSite=None; Secure — Cookie wird mitgesendet (alte Behavior); muss explizit gesetzt werden.

Konsequenz für klassisches CSRF: Der Auto-Submitting-Form-Angriff oben funktioniert seit 2020 bei den meisten Sites nicht mehr, weil das Bank-Session-Cookie standardmäßig Lax ist und bei einer Cross-Site-POST-Anfrage nicht mitgesendet wird.

Das ist eine massive Reduktion der CSRF-Angriffsfläche ohne Code-Änderung in den Anwendungen.

Vertieft in samesite-cookies.

Wo CSRF heute noch wichtig ist

Trotz SameSite-Defaults bleibt CSRF relevant in mehreren Konstellationen:

1. Bei expliziter SameSite=None-Konfiguration.

Wenn deine Anwendung bewusst Cookies cross-site funktionsfähig macht (z. B. für Cross-Domain-Auth, Embed-Workflows, OAuth-Flows), ist SameSite=None nötig — und die klassische CSRF-Angriffsfläche kommt zurück. CSRF-Tokens werden dann Pflicht.

2. „Lax + GET-Endpoints" als bekannter Bypass.

SameSite=Lax sendet Cookies bei Top-Level-GET-Navigation mit. Wenn deine Anwendung GET-Endpoints mit Seiteneffekten hat (z. B. GET /logout?user=42), bleibt CSRF möglich. Klassischer Anti-Pattern, der weiter existiert.

3. Bei Pre-2020-Browser-Nutzern.

In manchen Umgebungen (alte Embedded-Geräte, Enterprise-Setups mit eingefrorenen Browser-Versionen) sind SameSite-Defaults nicht aktiv. Wer solche Nutzer:innen erreicht, hat klassisches CSRF-Risiko.

4. In OAuth-Flows.

CSRF in OAuth-Authorization-Code-Flow ist ein eigenständiges Problem — der state-Parameter ist die Antwort. Vertieft in csrf-in-oauth-und-saml.

5. Innerhalb derselben Site.

Subdomains gelten in SameSite-Logik als „Same-Site" (bank.example und api.bank.example teilen sich Cookies). Wenn eine Subdomain XSS hat, kann sie CSRF gegen andere Subdomains ausführen. Subdomain-Hygiene wird damit zur Sicherheits-Frage.

6. Bei nicht-Cookie-Auth, die trotzdem implizit ist.

HTTP-Basic-Auth, NTLM, Kerberos-SPNEGO senden Credentials automatisch mit. SameSite gilt nur für Cookies, nicht für diese Mechanismen. Für interne Apps mit Windows-Auth bleibt CSRF-Risiko.

Diese Konstellationen sind in der Mehrheit aller Web-Apps selten — aber wer eine baut, muss CSRF-Schutz trotzdem implementieren. Defense-in-Depth heißt: nicht auf Browser-Default verlassen, sondern aktive Schutz-Schichten.

Verteidigungs-Schichten

Die Karte für die folgenden Artikel:

SchichtWas sie tutVertieft in
SameSite-CookiesBrowser-Enforcement: Cookies nicht cross-sitesamesite-cookies
CSRF-TokensPro Form/Request eindeutiger, geprüfter Tokencsrf-tokens-und-double-submit
Double-Submit-CookieToken im Cookie + im Body — Server prüft Übereinstimmungcsrf-tokens-und-double-submit
Origin/Referer-Header-CheckServer prüft, ob Request von erwarteter Origin kommtorigin-und-referer-header
Sec-Fetch-Site-HeaderModerne Browser-Auskunft zur Request-Herkunftorigin-und-referer-header
Custom-Header-AnforderungAPI verlangt einen Header, den nur eigenes JS setzen kanncsrf-in-spas-und-apis
OAuth state-ParameterAnti-CSRF im OAuth-Authorization-Flowcsrf-in-oauth-und-saml
Re-Authentifizierung bei kritischen AktionenMensch muss aktiv Passwort/2FA bestätigen(Kap 14)

In modernen Apps wirken meist mehrere Schichten zusammen — z. B. SameSite=Lax + CSRF-Token + Origin-Check. Eine einzelne Schicht reicht in Edge-Cases nicht.

CSRF in der OWASP Top 10

CSRF hatte in der OWASP Top 10 einen eigenen Platz von 2007 bis 2013 (jeweils unter den ersten 5). Seit OWASP Top 10 2017 ist CSRF nicht mehr in der Liste — die Begründung: moderne Framework-Defaults (CSRF-Token in Django, Rails, Spring, ASP.NET, Laravel, Express CSRF-Middleware) plus SameSite-Cookies haben die Klasse weitgehend gelöst.

Das heißt aber nicht, dass CSRF irrelevant ist — es heißt, dass es in den meisten modernen Apps strukturell schon mitgedacht ist. Pentest-Findings sind seltener, aber in Custom-Auth-Apps, in legacy-Microservices und in OAuth-Integrationen weiterhin regelmäßig zu finden.

Verwandt: Clickjacking (UI-Redress) wird oft mit CSRF verwechselt — beides nutzt Cross-Site-Mechanismen, aber Clickjacking braucht einen Klick der Nutzer:in auf einem überlagerten iframe. Detail in clickjacking-und-ui-redress (Kap 10).

Was du daraus mitnimmst

Praktische Konsequenzen für moderne Web-Apps:

  • SameSite=Lax als Default — moderne Browser machen es eh.
  • CSRF-Token für klassische Form-basierte Apps — Framework-Default in Django/Rails/Spring/Laravel.
  • Origin-Header-Check als zusätzliche Schicht — billig und wirksam.
  • Custom-Header-Anforderung für JSON-APIs — strukturell anti-CSRF.
  • GET-Endpoints ohne Side-Effects — RESTful-Disziplin schützt indirekt.
  • OAuth state-Parameter — Pflicht in jedem OAuth-Flow.
  • Re-Auth bei sensitiven Aktionen (Passwort-Wechsel, Auszahlung, OAuth-Verbindung) — fängt selbst CSRF auf legitimer Cookie-Auth ab.

Wenn alle Schichten aktiv sind, ist CSRF praktisch geschlossen. Aber: jede Schicht hat eigene Stolpersteine — und die folgenden Artikel zeigen sie konkret.

Interessantes

CSRF war 2008 ein populärer Bug-Bounty-Fund

Vor 2010 war CSRF unter den häufigsten Funden — Twitter, Facebook, Gmail, Hotmail hatten alle dokumentierte CSRF-Bugs. Die Aufmerksamkeit hat Framework-Entwickler:innen dazu gebracht, CSRF-Schutz standardmäßig einzubauen. Heute ist CSRF in Mainstream-Apps deutlich seltener — aber in OAuth-Flows und Custom-Auth bleibt es lebendig.

Das berühmte „2008-Netflix-CSRF"

Ein klassischer Lehrfall: Netflix erlaubte 2006, die Lieferadresse über eine GET-URL zu ändern. Ein:e Angreifer:in konnte mit einem versteckten <img>-Tag in einer fremden Seite Nutzer:innen-DVDs umleiten lassen. Der Vorfall ist in vielen Web-Sicherheits-Lehrbüchern dokumentiert — und ein klares Beispiel für „GET sollte nie Seiteneffekte haben".

SameSite-Lax-Default war Chromes Entscheidung 2020

Vor Februar 2020 war Cookies-Default SameSite=None (also cross-site mitgeschickt). Chrome hat den Default umgekippt — Firefox, Edge folgten in den nächsten Jahren. Eine der einflussreichsten Sicherheits-Änderungen der jüngeren Browser-Geschichte; viele Sites haben kurzzeitig Sub-Domain-Auth-Probleme erlebt.

Login-CSRF als eigene Klasse

Ein Sonderfall: Angreifer:in zwingt das Opfer, sich in einen Angreifer-kontrollierten Account einzuloggen. Folge: alle nachfolgenden Aktionen des Opfers in der Site werden im Angreifer-Account aufgezeichnet (Suchen, Käufe, persönliche Daten). Weniger destruktiv als klassisches CSRF, aber Privacy-relevant. Schutz: auch Login-Endpunkt verlangt CSRF-Token.

JSON-APIs sind im SOP-Modell schwerer angreifbar

Eine JSON-API, die Content-Type: application/json verlangt, ist klassisch durch SOP geschützt — der Browser löst Pre-Flight aus, der eine fremde Origin abblockt. Aber: Content-Type: text/plain oder multipart/form-data sind „simple requests", lösen keinen Pre-Flight aus — Angreifer:in kann sie cross-site senden.

CSRF auf Internet-of-Things

Lokale Router, Smart-Home-Hubs, Drucker mit Web-Interfaces — viele dieser Geräte haben keine CSRF-Schutz. Wenn eine fremde Webseite einen Request an http://192.168.1.1/setpassword?... schickt, läuft das im lokalen Netz. Klassische CSRF-Welle gegen Router 2012–2015; bis heute relevant in vernachlässigten Embedded-Geräten.

Re-Auth als Quasi-CSRF-Schutz

Sehr sensitive Aktionen (Passwort-Wechsel, Mail-Wechsel, OAuth-Authorize) verlangen oft, dass der/die Nutzer:in das Passwort erneut eingibt — auch wenn sie schon eingeloggt ist. Das ist neben anderem Zwecken (Konto-Übernahme verhindern, wenn ein Tab unattended ist) auch ein CSRF-Schutz: CSRF kann nicht das Passwort liefern.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu CSRF & SameSite

Zur Übersicht