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
- CsrfProtection: Verwaltet Token-Generierung und Validierung
- SessionManager: Bietet atomare Session-Updates mit Locking
- FormDataResponseProcessor: Ersetzt Token-Platzhalter in HTML
- CsrfMiddleware: Validiert Token bei state-changing Requests
- 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 EXfü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
- Token-Extraktion: Aus Request-Body oder Headers
- Form-ID-Extraktion: Aus Request-Body oder Headers
- Session-Lookup: Token in Session suchen
- Validierung: Token-Match und Expiration prüfen
- 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_idParameter 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
- Prüfe Token-Länge: Muss genau 64 Zeichen sein
- Prüfe Form-ID: Muss mit generierter Form-ID übereinstimmen
- Prüfe Session: Token muss in Session vorhanden sein
- Prüfe Expiration: Token darf nicht abgelaufen sein
Race Conditions
Das System verwendet atomare Session-Updates, um Race Conditions zu verhindern. Bei Problemen:
- Prüfe Session-Storage-Konfiguration
- Prüfe Locking-Mechanismus (File/Redis)
- Prüfe Logs für Version-Conflicts
HTML-Verarbeitung
Bei Problemen mit Token-Ersetzung:
- Prüfe Platzhalter-Format:
___TOKEN_FORMID___ - Prüfe HTML-Struktur (DOM-Parsing kann bei malformed HTML fehlschlagen)
- 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
- Verwende
/api/csrf/tokenstatt/api/csrf-token - Nutze atomare Session-Updates für kritische Operationen
- 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
- Token-Rotation: Neue Token nach jeder Generierung
- Expiration: Token laufen nach 2 Stunden ab
- Single-Use: Token können innerhalb von 30 Sekunden wiederverwendet werden
- 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\CsrfProtectionApp\Framework\Http\Session\SessionManagerApp\Framework\View\Response\FormDataResponseProcessorApp\Framework\Http\Middlewares\CsrfMiddlewareApp\Application\Api\CsrfControllerApp\Application\Controller\CsrfController
Konstanten
CsrfProtection::TOKEN_LIFETIME= 7200 (2 Stunden)CsrfProtection::MAX_TOKENS_PER_FORM= 3CsrfProtection::RE_SUBMIT_WINDOW= 30 (Sekunden)