Synchronizer Token, SameSite und Magentos form_key im Zusammenspiel
Wer Formulare und State-Change-Endpunkte ohne echten CSRF-Schutz betreibt, verlässt sich allein auf die Session-Cookie des Browsers und öffnet damit ein Einfallstor für fremdgesteuerte Requests. Synchronizer Token, Double-Submit-Cookie, SameSite-Attribut und Magentos form_key zeigen, wie sich das zuverlässig schließen lässt.
Inhaltsverzeichnis
- 1. Was CSRF ist und wie es die automatische Cookie-Übermittlung ausnutzt
- 2. Anatomie eines CSRF-Angriffs
- 3. Das Synchronizer Token Pattern: Mechanik und Validierung
- 4. Double-Submit-Cookie als zustandsloser Ansatz
- 5. SameSite-Cookie-Attribut als ergänzende Verteidigung
- 6. Warum GET-Requests niemals Seiteneffekte haben dürfen
- 7. Magentos form_key CSRF-Schutz im Detail
- 8. CSRF-Schutz für AJAX, SPAs und API-Endpunkte
- 9. CSRF-Verteidigungsmechanismen im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was CSRF ist und wie es die automatische Cookie-Übermittlung ausnutzt
Cross-Site Request Forgery (CSRF) nutzt eine Grundeigenschaft des Browsers aus, die für normale Nutzung unverzichtbar ist: Bei jedem Request an eine Domain hängt der Browser automatisch alle passenden Cookies an, unabhängig davon, welche Seite den Request ausgelöst hat. Ist ein Nutzer bei einer Anwendung angemeldet und besitzt eine gültige Session-Cookie, sendet der Browser diese Cookie auch dann mit, wenn der Request von einer völlig fremden Webseite stammt. Der Server sieht am Ende nur einen authentifizierten Request und kann ohne zusätzliche Prüfung nicht unterscheiden, ob der Nutzer diesen Request bewusst ausgelöst hat.
Der entscheidende Unterschied zu Cross-Site Scripting (XSS): Der Angreifer muss die Session-Cookie weder lesen noch stehlen. Die Same-Origin-Policy verhindert zwar, dass fremder JavaScript-Code die Antwort einer anderen Domain lesen kann, sie verhindert aber nicht, dass ein Request überhaupt gesendet wird. Genau diese Lücke zwischen „Antwort lesen" und „Request senden" macht CSRF zu einer eigenständigen Bedrohung, die eine eigene Verteidigungsstrategie braucht, unabhängig von XSS-Schutzmaßnahmen wie Content-Security-Policy oder Output-Escaping.
2. Anatomie eines CSRF-Angriffs
Ein typischer CSRF-Angriff läuft konzeptionell in drei Schritten ab, ohne dass die Zielanwendung selbst eine Schwachstelle wie XSS aufweisen muss. Erstens: Ein Opfer meldet sich bei einer Zielanwendung an, zum Beispiel einem Onlineshop-Backend, und erhält eine Session-Cookie. Zweitens: Während die Session noch aktiv ist, besucht das Opfer eine vom Angreifer kontrollierte oder kompromittierte Webseite. Drittens: Diese Seite enthält ein verstecktes Formular oder einen automatisch feuernden Request, der auf eine state-verändernde URL der Zielanwendung zeigt, etwa eine Endpunkt zum Ändern der E-Mail-Adresse oder zum Anlegen eines neuen Admin-Benutzers.
Sobald der Browser des Opfers diesen Request absetzt, hängt er automatisch die gültige Session-Cookie an, und der Server der Zielanwendung führt die Aktion im Namen des Opfers aus, ohne dass dieses etwas davon bemerkt. Wichtig für das Verständnis: Dieser Ablauf beschreibt das Angriffsmuster rein konzeptionell, um Verteidigungsmaßnahmen einordnen zu können, nicht als funktionierende Anleitung. Entscheidend ist, dass klassische Authentifizierung über Cookies allein niemals ausreicht, um einen Request als bewusst vom Nutzer initiiert zu bestätigen.
3. Das Synchronizer Token Pattern: Mechanik und Validierung
Das Synchronizer Token Pattern ist die etablierteste CSRF-Verteidigung. Der Server generiert einen kryptographisch zufälligen, unvorhersagbaren Token, speichert ihn serverseitig an die aktive Session gebunden und bettet ihn als verstecktes Feld in jedes Formular ein. Beim Abschicken des Formulars sendet der Browser den Token als Teil des Request-Bodys mit. Weil der Angreifer diesen Token weder kennt noch aus einer fremden Seite auslesen kann, die Same-Origin-Policy verhindert genau das, kann er keinen gültigen Request fälschen, selbst wenn die Session-Cookie automatisch mitgeschickt wird.
Entscheidend ist die korrekte serverseitige Validierung: Der übermittelte Token muss mit dem gespeicherten Wert über einen zeitkonstanten Vergleich (etwa hash_equals() in PHP) geprüft werden, niemals mit einem einfachen ==, das durch Timing-Angriffe angreifbar wäre. Der Token sollte bei jedem Login neu generiert und nach erfolgreicher Nutzung im Idealfall rotiert werden, um Replay-Angriffe über gestohlene Logs oder Browser-Historie zu erschweren. Fehlt die Validierung in nur einem einzigen state-verändernden Endpunkt, ist der gesamte Schutz für diesen Pfad wirkungslos.
<!-- Hidden CSRF token field embedded in a state-changing form -->
<form method="POST" action="/account/email/update">
<label for="email">New email address</label>
<input type="email" id="email" name="email" required>
<!-- Synchronizer token bound to the current server-side session -->
<input type="hidden" name="csrf_token" value="a1b2c3d4e5f6...">
<button type="submit">Update email</button>
</form>
4. Double-Submit-Cookie als zustandsloser Ansatz
Das Double-Submit-Cookie-Pattern löst dasselbe Problem ohne serverseitigen Session-Speicher für den Token. Der Server setzt einen zufälligen Token als Cookie, und derselbe Token muss zusätzlich als Request-Parameter oder Custom-Header mitgesendet werden. Der Server prüft nur, ob beide Werte übereinstimmen. Weil ein Angreifer den Cookie-Wert einer fremden Domain wegen der Same-Origin-Policy nicht auslesen kann, um ihn in den Parameter zu kopieren, scheitert eine gefälschte Anfrage an der fehlenden Übereinstimmung. Dieser Ansatz eignet sich besonders für zustandslose APIs und verteilte Systeme, in denen kein zentraler Session-Speicher existiert.
Die einfache Variante hat eine bekannte Schwäche: Kann ein Angreifer über eine Subdomain oder eine unsichere Cookie-Konfiguration einen eigenen Cookie mit demselben Namen setzen (Cookie Tossing), lässt sich der Doppel-Check aushebeln. Die robustere Variante signiert den Token daher zusätzlich mit einem serverseitigen Geheimnis (HMAC), sodass ein untergeschobener Cookie ohne gültige Signatur sofort auffällt. In Kombination mit __Host--Cookie-Präfixen, die Domain-Übergreifendes Setzen technisch verhindern, wird das Verfahren deutlich robuster.
<?php
declare(strict_types=1);
/**
* Validates a double-submit CSRF token using an HMAC-signed cookie value.
* Compares the signed cookie value against the value sent in the request header.
*/
final class DoubleSubmitCsrfValidator
{
public function __construct(private readonly string $hmacSecret)
{
}
public function isValid(?string $cookieToken, ?string $headerToken): bool
{
if ($cookieToken === null || $headerToken === null) {
return false;
}
// Constant-time comparison prevents timing side-channel attacks
if (!hash_equals($cookieToken, $headerToken)) {
return false;
}
[$rawToken, $signature] = array_pad(explode('.', $cookieToken, 2), 2, '');
$expectedSignature = hash_hmac('sha256', $rawToken, $this->hmacSecret);
return hash_equals($expectedSignature, $signature);
}
}
5. SameSite-Cookie-Attribut als ergänzende Verteidigung
Das SameSite-Attribut weist den Browser an, Cookies bei Cross-Site-Requests gar nicht erst mitzuschicken. Strict unterbindet den Versand vollständig bei jedem Cross-Site-Navigationsversuch, auch beim Klick auf einen normalen Link von einer fremden Seite, was für Session-Cookies bei sicherheitskritischen Anwendungen sinnvoll sein kann, aber die Nutzererfahrung einschränkt. Lax, der heutige Standardwert in modernen Browsern, erlaubt den Versand bei einfachen Top-Level-Navigationen wie GET-Links, blockiert ihn aber bei Formular-Submits per POST und bei eingebetteten Ressourcen wie iframes oder Bildern. None deaktiviert die Beschränkung vollständig und erfordert zwingend das Secure-Attribut.
SameSite ist aber ausdrücklich keine Ersetzung für Token-basierten CSRF-Schutz, sondern eine ergänzende Verteidigungsschicht. Ältere Browser ignorieren das Attribut komplett und senden Cookies wie gewohnt. Subdomain-Angriffe, bei denen ein Angreifer eine andere Subdomain derselben Registrierbaren Domain kontrolliert, gelten je nach Browser-Implementierung teils noch als „same-site" und umgehen den Schutz. Und GET-basierte State-Changes, die entgegen der HTTP-Semantik existieren, feuern unter Lax weiterhin. Defense in Depth bedeutet: SameSite reduziert die Angriffsfläche spürbar, ersetzt aber niemals die explizite Token-Validierung.
# Nginx: enforce Secure and SameSite attributes on the session cookie
# proxied from the application, as a defense-in-depth layer alongside
# explicit CSRF token validation in the application code
proxy_cookie_flags session_id secure samesite=lax;
# PHP: configure session cookie params before session_start()
<?php
declare(strict_types=1);
// Configure the session cookie with SameSite as a complementary defense,
// not as a replacement for synchronizer token validation
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
6. Warum GET-Requests niemals Seiteneffekte haben dürfen
Die HTTP-Spezifikation definiert GET als sichere Methode (safe method), die keine Seiteneffekte auf dem Server auslösen darf, und als idempotent, das heißt beliebig oft wiederholbar mit demselben Ergebnis. Browser, Proxies, Crawler und Prefetching-Mechanismen verlassen sich auf diese Garantie und lösen GET-Requests automatisch und ungefragt aus, etwa beim Vorabladen von Links, beim erneuten Laden einer Seite oder beim Indexieren durch Suchmaschinen-Bots. Historisch bekannte CSRF-Vorfälle nutzten genau das aus: Ein simples <img src="/account/delete?id=42"> auf einer fremden Seite genügte, um eine Löschaktion auszulösen, weil der Endpunkt fälschlicherweise auf GET reagierte.
Wer state-verändernde Operationen konsequent auf POST, PUT, PATCH oder DELETE beschränkt, entzieht CSRF-Angriffen über einfache Links, Bilder oder Prefetching von vornherein die Grundlage, weil diese Mechanismen ausschließlich GET auslösen. Das ist keine vollständige CSRF-Verteidigung, denn auch POST-Requests lassen sich über automatisch absendende Formulare fälschen, aber es ist die absolute Grundvoraussetzung. REST-konforme API-Designs, die GET strikt als lesend behandeln, schließen damit eine ganze Klasse trivialer Angriffe aus, bevor überhaupt Token-Validierung ins Spiel kommt.
7. Magentos form_key CSRF-Schutz im Detail
Magento implementiert das Synchronizer Token Pattern über den sogenannten form_key. Beim Start einer Session generiert Magento\Framework\Data\Form\FormKey einen zufälligen Token und speichert ihn in der Session. In jedem Formular wird der Wert über $block->getFormKey() beziehungsweise das eingebaute form_key.phtml-Template als verstecktes Feld ausgegeben. Für klassische Controller, die von Magento\Framework\App\Action\Action erben, prüft Magento\Framework\Data\Form\FormKey\Validator bei POST-Requests automatisch, ob der übermittelte form_key mit dem Session-Wert übereinstimmt, sofern der Controller nicht explizit _isAllowed() ohne diese Prüfung überschreibt.
Seit Magento 2.3 kommt für neuere REST- und Controller-Endpunkte zusätzlich Magento\Framework\App\CsrfAwareActionInterface zum Einsatz, das createCsrfValidationException() und validateForCsrf() vorschreibt. Der häufigste Fehler bei neuen Controllern: Wird validateForCsrf() unreflektiert mit return true; implementiert, weil ein Entwickler die Prüfung „vorübergehend" deaktivieren wollte, bleibt der Endpunkt dauerhaft ungeschützt. Ebenso häufig vergessen: Custom-AJAX-Controller, die nicht von der Standard-Actionklasse erben, sondern direkt HttpPostActionInterface implementieren, müssen die CSRF-Validierung explizit selbst einbinden, sie erben sie nicht automatisch.
<?php
declare(strict_types=1);
namespace Mironsoft\Example\Controller\Adminhtml\Item;
use Magento\Backend\App\Action;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\RequestInterface;
/**
* Controller demonstrating explicit CSRF validation for a new admin endpoint.
* Extending Action alone is not sufficient once CsrfAwareActionInterface
* is implemented explicitly; validateForCsrf() must perform a real check.
*/
class Delete extends Action implements HttpPostActionInterface, CsrfAwareActionInterface
{
/**
* Rejects the request with a CSRF exception when validation fails.
*
* @param RequestInterface $request
* @return InvalidRequestException|null
*/
public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
{
return null;
}
/**
* Performs the actual form_key comparison against the session value.
* Returning true unconditionally here disables CSRF protection entirely.
*
* @param RequestInterface $request
* @return bool|null
*/
public function validateForCsrf(RequestInterface $request): ?bool
{
// Delegate to the framework's default form_key validation logic
return null;
}
/**
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
// Item deletion logic runs only after CSRF validation has passed
return $this->resultRedirectFactory->create()->setPath('*/*/');
}
}
8. CSRF-Schutz für AJAX, SPAs und API-Endpunkte
Auch AJAX-Requests, die von derselben Origin stammen, tragen die Session-Cookie automatisch mit und brauchen daher denselben CSRF-Schutz wie klassische Formulare. Das gängige Pattern: Der Server rendert den Token beim initialen Seitenaufbau in ein <meta>-Tag oder eine dedizierte JSON-Konfiguration, das Frontend liest ihn aus und sendet ihn bei jedem state-verändernden Request als Custom-Header, etwa X-CSRF-Token, statt im Body. Der entscheidende Vorteil von Custom-Headern gegenüber Body-Parametern: Custom-Header lassen sich aus reinem HTML heraus, ohne JavaScript, gar nicht setzen, ein simples HTML-Formular auf einer Angreiferseite kann sie prinzipbedingt nicht fälschen.
Bei Single-Page-Applications empfiehlt sich ein dedizierter Token-Endpunkt, der beim App-Start oder nach Login aufgerufen wird und den aktuellen Token liefert, kombiniert mit Rotation nach jedem sensiblen Vorgang. Reine API-Endpunkte für Drittsysteme, die über API-Keys oder OAuth-Bearer-Token statt Cookies authentifizieren, benötigen in der Regel keinen CSRF-Schutz, weil der Browser Bearer-Token aus Authorization-Headern niemals automatisch mitschickt, das ist genau der Unterschied zur Cookie-basierten Authentifizierung, der CSRF überhaupt erst ermöglicht.
// Read the CSRF token from a meta tag rendered by the server
// and send it as a custom header on every state-changing fetch request
function getCsrfToken() {
const meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.getAttribute('content') : null;
}
async function updateAccountEmail(email) {
const response = await fetch('/api/account/email', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken(),
},
body: JSON.stringify({ email }),
});
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
return response.json();
}
9. CSRF-Verteidigungsmechanismen im Vergleich
Keine einzelne Maßnahme deckt alle CSRF-Angriffsvektoren vollständig ab. Erst das Zusammenspiel aus Token-Validierung, sauberer HTTP-Methoden-Semantik und Cookie-Attributen ergibt eine belastbare Verteidigung. Die folgende Übersicht stellt unsichere Standardannahmen den empfohlenen, sicheren Ansätzen gegenüber.
| Aspekt | Unsicher | Sicherer Ansatz | Begründung |
|---|---|---|---|
| Authentifizierung prüfen | Nur gültige Session-Cookie als Nachweis | Session-Cookie + Synchronizer Token | Cookie allein bestätigt keine bewusste Nutzeraktion |
| State-Change auslösen | Über GET-Links wie /delete?id=42 | Ausschließlich über POST/PUT/DELETE | GET wird automatisch von Browsern und Bots ausgelöst |
| Token-Vergleich | Einfacher ==-Stringvergleich |
hash_equals(), zeitkonstant |
Verhindert Timing-basierte Seitenkanalangriffe |
| Cookie-Konfiguration | SameSite=None ohne weitere Prüfung | SameSite=Lax/Strict + Token-Validierung | SameSite ergänzt Token-Schutz, ersetzt ihn nicht |
| AJAX-Token-Übertragung | Token nur im Request-Body, wie normale Formularfelder | Custom-Header wie X-CSRF-Token |
Custom-Header lassen sich ohne JavaScript nicht setzen |
| Magento-Controller | validateForCsrf() gibt hart true zurück |
Echte form_key-Prüfung über FormKey\Validator |
Hartkodiertes true deaktiviert den Schutz dauerhaft |
Mironsoft
Web Application Security, Code-Audits und Magento-Absicherung
CSRF-Lücken zuverlässig schließen?
Wir prüfen eure Formulare, AJAX-Endpunkte und Magento-Controller auf fehlende oder fehlerhafte CSRF-Validierung und setzen Token-Pattern, SameSite-Konfiguration und form_key-Absicherung sauber um.
CSRF-Audit
Systematische Prüfung aller state-verändernden Endpunkte und Controller
Magento-Härtung
form_key- und CsrfAwareActionInterface-Implementierung korrekt nachrüsten
API-Absicherung
Token-Header-Pattern für AJAX, SPAs und REST-Endpunkte einführen
10. Zusammenfassung
CSRF-Schutz richtig implementieren bedeutet, die automatische Cookie-Übermittlung des Browsers als gegeben zu akzeptieren und sie nicht als Authentifizierungsnachweis für eine bewusste Nutzeraktion zu missbrauchen. Das Synchronizer Token Pattern bleibt die zuverlässigste Methode, weil es einen Wert erfordert, den ein Angreifer prinzipiell nicht erraten oder auslesen kann. Double-Submit-Cookies bieten eine zustandslose Alternative für verteilte Systeme, solange sie zusätzlich signiert werden. SameSite reduziert die Angriffsfläche spürbar, ersetzt aber niemals die explizite Validierung im Anwendungscode.
Genauso wichtig wie die Token-Mechanik ist die konsequente Trennung von lesenden und schreibenden HTTP-Methoden: GET darf niemals Seiteneffekte auslösen. In Magento übernimmt der form_key diese Aufgabe für klassische Controller automatisch, aber jeder neue Custom-Controller und jeder AJAX-Endpunkt muss die Validierung explizit korrekt implementieren, statt sie versehentlich mit einem hartkodierten true zu deaktivieren.
CSRF-Schutz richtig implementieren, das Wichtigste auf einen Blick
Synchronizer Token
Server generiert unvorhersagbaren Token pro Session, Validierung über hash_equals(), niemals mit ==.
SameSite als Ergänzung
Lax oder Strict reduziert die Angriffsfläche, ersetzt aber niemals die Token-Validierung im Code.
GET bleibt lesend
State-Changes ausschließlich über POST/PUT/DELETE, niemals über GET-Links oder Bild-Tags auslösbar.
Magento form_key
FormKey\Validator für Standard-Controller, CsrfAwareActionInterface korrekt implementieren bei Custom-Endpunkten.