fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
365
docs/framework/csrf-protection.md
Normal file
365
docs/framework/csrf-protection.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# 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.
|
||||
|
||||
```php
|
||||
// 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:
|
||||
|
||||
```php
|
||||
// Automatische Ersetzung von Platzhaltern
|
||||
$html = $processor->process($html, $session);
|
||||
```
|
||||
|
||||
### Platzhalter-Format
|
||||
|
||||
```html
|
||||
<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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```php
|
||||
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
|
||||
|
||||
```php
|
||||
// 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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user