CSRF-Schutz richtig implementieren
OWASP
0x00
Security · CSRF · Web Application Security · Magento 2
CSRF-Schutz richtig implementieren
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.

16 Min. Lesezeit Synchronizer Token · SameSite · form_key OWASP · Magento 2.4.8 · REST APIs

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.

11. FAQ: CSRF-Schutz richtig implementieren

1Was ist CSRF und wie unterscheidet es sich von XSS?
CSRF zwingt den Browser eines angemeldeten Nutzers, unbemerkt einen Request zu senden, wobei die Session-Cookie automatisch mitgeschickt wird. XSS injiziert dagegen fremden Code, um Daten auszulesen oder zu manipulieren.
2Warum kann der Angreifer den Request auslösen, ohne die Cookie zu kennen?
Der Browser hängt Cookies automatisch an jeden passenden Request an, egal welche Seite ihn ausgelöst hat. Der Angreifer muss die Cookie nicht lesen, nur den Browser zum Senden bringen.
3Reicht SameSite=Lax als alleiniger CSRF-Schutz aus?
Nein. Ältere Browser ignorieren das Attribut, Subdomain-Konstellationen können es umgehen, und GET-basierte State-Changes bleiben unter Lax erlaubt. Nur eine ergänzende Schicht, kein Ersatz für Token-Validierung.
4Wie funktioniert das Synchronizer Token Pattern genau?
Der Server generiert einen zufälligen Token, speichert ihn an die Session gebunden und bettet ihn in jedes Formular ein. Beim Absenden erfolgt ein zeitkonstanter Vergleich mit dem gespeicherten Wert.
5Unterschied zwischen Synchronizer Token und Double-Submit-Cookie?
Der Synchronizer Token wird serverseitig in der Session gespeichert. Double-Submit-Cookie speichert keinen Zustand, sondern vergleicht Cookie und Request-Header, idealerweise zusätzlich HMAC-signiert.
6Warum dürfen GET-Requests keine Seiteneffekte haben?
GET gilt als sichere, idempotente Methode. Browser, Crawler und Prefetching lösen GET automatisch aus. Ein Seiteneffekt in GET lässt sich schon über ein einfaches Bild-Tag auslösen.
7Wie funktioniert Magentos form_key CSRF-Schutz?
FormKey generiert beim Sessionstart einen Token, der in Formularen ausgegeben und bei POST-Requests über FormKey\Validator geprüft wird. Seit 2.3 übernimmt CsrfAwareActionInterface diese Aufgabe für neuere Controller.
8Häufigster Fehler bei neuen Magento-Controllern bezüglich CSRF?
validateForCsrf() mit hartkodiertem return true implementieren und vor dem Deployment vergessen zu korrigieren. Custom-AJAX-Controller ohne Standard-Basisklasse vergessen die Validierung oft ganz.
9Wie schützt man AJAX/SPA-Endpunkte vor CSRF?
Token in ein Meta-Tag rendern oder über einen dedizierten Endpunkt liefern, dann bei jedem Request als Custom-Header wie X-CSRF-Token senden. Reines HTML kann Custom-Header nicht setzen.
10Schützt CORS automatisch vor CSRF?
Nein. CORS regelt nur, ob fremdes JavaScript die Antwort lesen darf, nicht ob der Request gesendet wird. Ein HTML-Formular ohne JavaScript unterliegt keiner CORS-Prüfung.