Files
michaelschiemer/docs/framework/csrf-protection.md
2025-11-24 21:28:25 +01:00

8.9 KiB

CSRF Protection System

Übersicht

Das CSRF (Cross-Site Request Forgery) Protection System bietet robusten Schutz gegen CSRF-Angriffe durch:

  • Atomare Session-Updates: Verhindert Race Conditions bei parallelen Requests
  • Vereinfachte Token-Generierung: Immer neue Token, keine Wiederverwendung
  • Robuste HTML-Verarbeitung: DOM-basierte Token-Ersetzung mit Regex-Fallback
  • Einheitliche API: Konsistente Endpunkte für PHP und JavaScript

Architektur

Komponenten

  1. CsrfProtection: Verwaltet Token-Generierung und Validierung
  2. SessionManager: Bietet atomare Session-Updates mit Locking
  3. FormDataResponseProcessor: Ersetzt Token-Platzhalter in HTML
  4. CsrfMiddleware: Validiert Token bei state-changing Requests
  5. API Controllers: Bereitstellen Token-Endpunkte für JavaScript

Session-Locking

Das System verwendet Session-Locking, um Race Conditions zu verhindern:

  • FileSessionStorage: Datei-basiertes Locking mit flock()
  • RedisSessionStorage: Redis SET NX EX für atomares Locking
  • InMemorySessionStorage: In-Memory-Locking für Tests

Optimistic Locking

SessionData verwendet Versionsnummern für Optimistic Locking:

  • Jede Änderung inkrementiert die Version
  • Bei Konflikten wird automatisch retried
  • Verhindert Datenverlust bei parallelen Updates

Token-Generierung

Vereinfachte Strategie

Wichtig: Das System generiert immer neue Token - keine Wiederverwendung.

// Generiert immer einen neuen Token
$token = $session->csrf->generateToken($formId);

Token-Verwaltung

  • Maximal 3 Token pro Form-ID: Älteste werden automatisch entfernt
  • Token-Lifetime: 2 Stunden (7200 Sekunden)
  • Re-Submit Window: 30 Sekunden für erneute Submissions
  • Automatisches Cleanup: Abgelaufene Token werden entfernt

Token-Format

  • Länge: 64 Zeichen
  • Format: Hexadezimal (0-9, a-f)
  • Beispiel: f677a410facd19e4e004e41e24fa1c8abbe2379a91abcf8642de23a6988ba8b

HTML-Verarbeitung

DOM-basierte Ersetzung

Der FormDataResponseProcessor verwendet DOM-basierte Verarbeitung für robuste Token-Ersetzung:

// Automatische Ersetzung von Platzhaltern
$html = $processor->process($html, $session);

Platzhalter-Format

<input type="hidden" name="_token" value="___TOKEN_FORMID___">

Fallback-Mechanismus

Bei DOM-Parsing-Fehlern fällt das System automatisch auf Regex-basierte Ersetzung zurück.

API-Endpunkte

Token-Generierung

GET/POST /api/csrf/token

Generiert einen neuen CSRF-Token für eine Form.

Parameter:

  • action (optional): Form-Action URL (Standard: /)
  • method (optional): HTTP-Methode (Standard: post)
  • form_id (optional): Explizite Form-ID

Response:

{
  "form_id": "form_abc123def456",
  "token": "64-character-hex-token",
  "expires_in": 7200,
  "headers": {
    "X-CSRF-Form-ID": "form_abc123def456",
    "X-CSRF-Token": "64-character-hex-token"
  }
}

Token-Refresh

GET /api/csrf/refresh

Generiert einen neuen Token für eine bestehende Form-ID.

Parameter:

  • form_id (erforderlich): Form-ID

Response:

{
  "form_id": "form_abc123def456",
  "token": "64-character-hex-token",
  "expires_in": 7200,
  "headers": {
    "X-CSRF-Form-ID": "form_abc123def456",
    "X-CSRF-Token": "64-character-hex-token"
  }
}

Token-Informationen

GET /api/csrf/info

Gibt Informationen über aktive Token zurück.

Parameter:

  • form_id (erforderlich): Form-ID

Response:

{
  "form_id": "form_abc123def456",
  "active_tokens": 2,
  "max_tokens_per_form": 3,
  "token_lifetime_seconds": 7200,
  "resubmit_window_seconds": 30
}

JavaScript-Integration

CsrfManager

Der CsrfManager verwaltet Token automatisch:

import { CsrfManager } from './modules/security/CsrfManager.js';

const csrfManager = CsrfManager.create({
    endpoint: '/api/csrf/token',
    autoRefresh: true,
    refreshInterval: 30 * 60 * 1000 // 30 Minuten
});

// Token abrufen
const token = csrfManager.getToken();

// Token-Header für Requests
const headers = csrfManager.getTokenHeader();

Manuelle Token-Anfrage

// Token für spezifische Form generieren
const response = await fetch('/api/csrf/token?action=/submit&method=post');
const data = await response.json();

const token = data.token;
const formId = data.form_id;

AJAX-Requests

// Token in Request-Body
fetch('/api/submit', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Form-ID': formId,
        'X-CSRF-Token': token
    },
    body: JSON.stringify({
        _form_id: formId,
        _token: token,
        // ... weitere Daten
    })
});

Validierung

Middleware

Die CsrfMiddleware validiert automatisch Token für:

  • POST Requests
  • PUT Requests
  • DELETE Requests
  • PATCH Requests

GET Requests werden nicht validiert.

Validierungsprozess

  1. Token-Extraktion: Aus Request-Body oder Headers
  2. Form-ID-Extraktion: Aus Request-Body oder Headers
  3. Session-Lookup: Token in Session suchen
  4. Validierung: Token-Match und Expiration prüfen
  5. Markierung: Token als verwendet markieren

Fehlerbehandlung

Bei Validierungsfehlern wird eine CsrfValidationFailedException geworfen mit:

  • Fehlgrund (missing token, invalid token, expired, etc.)
  • Session-ID
  • Anzahl verfügbarer Token
  • Liste verfügbarer Form-IDs

Best Practices

Form-IDs

  • Verwende konsistente Form-IDs für dieselben Formulare
  • Form-IDs werden automatisch aus Action und Method generiert
  • Explizite Form-IDs können über form_id Parameter gesetzt werden

Token-Refresh

  • Token sollten vor Ablauf erneuert werden
  • Automatischer Refresh alle 30 Minuten empfohlen
  • Bei langen Formularen manuell vor Submit refreshen

Fehlerbehandlung

try {
    // Form-Verarbeitung
} catch (CsrfValidationFailedException $e) {
    // Token ungültig - Benutzer sollte Seite neu laden
    return redirect()->back()->with('error', 'Session expired. Please try again.');
}

Testing

// Token generieren
$token = $session->csrf->generateToken('test-form');

// Token validieren
$isValid = $session->csrf->validateToken('test-form', $token);

// Mit Debug-Informationen
$result = $session->csrf->validateTokenWithDebug('test-form', $token);
if (!$result['valid']) {
    $reason = $result['debug']['reason'];
    // ...
}

Troubleshooting

Token wird nicht akzeptiert

  1. Prüfe Token-Länge: Muss genau 64 Zeichen sein
  2. Prüfe Form-ID: Muss mit generierter Form-ID übereinstimmen
  3. Prüfe Session: Token muss in Session vorhanden sein
  4. Prüfe Expiration: Token darf nicht abgelaufen sein

Race Conditions

Das System verwendet atomare Session-Updates, um Race Conditions zu verhindern. Bei Problemen:

  1. Prüfe Session-Storage-Konfiguration
  2. Prüfe Locking-Mechanismus (File/Redis)
  3. Prüfe Logs für Version-Conflicts

HTML-Verarbeitung

Bei Problemen mit Token-Ersetzung:

  1. Prüfe Platzhalter-Format: ___TOKEN_FORMID___
  2. Prüfe HTML-Struktur (DOM-Parsing kann bei malformed HTML fehlschlagen)
  3. Prüfe Logs für Fallback auf Regex

Migration

Von altem System

Das neue System ist vollständig kompatibel mit dem alten System:

  • Alte Token werden weiterhin akzeptiert
  • Alte API-Endpunkte funktionieren weiterhin
  • Keine Breaking Changes

Neue Features nutzen

  1. Verwende /api/csrf/token statt /api/csrf-token
  2. Nutze atomare Session-Updates für kritische Operationen
  3. Verwende DOM-basierte HTML-Verarbeitung (automatisch)

Performance

Optimierungen

  • Token-Cleanup: Automatisch nach 5 Minuten
  • Maximal 3 Token: Begrenzt Session-Größe
  • Locking-Timeout: 5 Sekunden Standard-Timeout
  • Optimistic Locking: Reduziert Lock-Contention

Monitoring

  • Token-Anzahl pro Form-ID überwachen
  • Session-Lock-Contention überwachen
  • Token-Validierungs-Fehlerrate überwachen

Sicherheit

Schutz-Mechanismen

  1. Token-Rotation: Neue Token nach jeder Generierung
  2. Expiration: Token laufen nach 2 Stunden ab
  3. Single-Use: Token können innerhalb von 30 Sekunden wiederverwendet werden
  4. Session-Binding: Token sind an Session gebunden

Angriffs-Vektoren

Das System schützt gegen:

  • CSRF-Angriffe: Token-Validierung verhindert unautorisierte Requests
  • Token-Replay: Expiration und Single-Use verhindern Replay
  • Session-Hijacking: Session-Binding verhindert Token-Diebstahl

Referenz

Klassen

  • App\Framework\Http\Session\CsrfProtection
  • App\Framework\Http\Session\SessionManager
  • App\Framework\View\Response\FormDataResponseProcessor
  • App\Framework\Http\Middlewares\CsrfMiddleware
  • App\Application\Api\CsrfController
  • App\Application\Controller\CsrfController

Konstanten

  • CsrfProtection::TOKEN_LIFETIME = 7200 (2 Stunden)
  • CsrfProtection::MAX_TOKENS_PER_FORM = 3
  • CsrfProtection::RE_SUBMIT_WINDOW = 30 (Sekunden)