- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
418 lines
11 KiB
Markdown
418 lines
11 KiB
Markdown
# CSRF-Schutz
|
|
|
|
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung des CSRF-Schutzes korrekt dar.
|
|
|
|
## Übersicht
|
|
|
|
Cross-Site Request Forgery (CSRF) ist eine Art von Angriff, bei dem ein Angreifer einen Benutzer dazu bringt, unbeabsichtigt Aktionen auf einer Website auszuführen, bei der er bereits authentifiziert ist. Das Framework bietet einen robusten CSRF-Schutzmechanismus, der solche Angriffe verhindert, indem es einzigartige Token generiert und validiert.
|
|
|
|
## Hauptkomponenten
|
|
|
|
### CsrfToken
|
|
|
|
Die `CsrfToken`-Klasse ist ein Value Object, das einen CSRF-Token repräsentiert:
|
|
|
|
```php
|
|
use App\Framework\Security\CsrfToken;
|
|
|
|
// Token aus einem String erstellen
|
|
$token = CsrfToken::fromString($tokenString);
|
|
|
|
// Token-Wert als String abrufen
|
|
$tokenString = $token->toString();
|
|
// oder
|
|
$tokenString = (string)$token;
|
|
```
|
|
|
|
Die Klasse stellt sicher, dass CSRF-Token immer gültige 64-Zeichen lange hexadezimale Strings sind und bietet Typsicherheit für CSRF-Token-Operationen.
|
|
|
|
### CsrfTokenGenerator
|
|
|
|
Die `CsrfTokenGenerator`-Klasse ist verantwortlich für die Generierung neuer CSRF-Token:
|
|
|
|
```php
|
|
use App\Framework\Security\CsrfTokenGenerator;
|
|
|
|
// Generator initialisieren
|
|
$generator = new CsrfTokenGenerator($randomGenerator);
|
|
|
|
// Neues Token generieren
|
|
$token = $generator->generate();
|
|
```
|
|
|
|
Die Klasse verwendet einen kryptografisch sicheren Zufallszahlengenerator, um Token zu erzeugen, die nicht vorhersehbar sind.
|
|
|
|
### CsrfTokenData
|
|
|
|
Die `CsrfTokenData`-Klasse erweitert das einfache Token um zusätzliche Metadaten:
|
|
|
|
```php
|
|
use App\Framework\Security\CsrfTokenData;
|
|
|
|
// Token-Daten mit Metadaten erstellen
|
|
$tokenData = new CsrfTokenData(
|
|
$token,
|
|
$clientIp,
|
|
$targetPath,
|
|
$expiresAt
|
|
);
|
|
|
|
// Prüfen, ob das Token abgelaufen ist
|
|
if ($tokenData->isExpired($clock)) {
|
|
// Token ist abgelaufen
|
|
}
|
|
|
|
// Prüfen, ob das Token bereits verwendet wurde
|
|
if ($tokenData->isUsed()) {
|
|
// Token wurde bereits verwendet
|
|
}
|
|
```
|
|
|
|
Diese Klasse ermöglicht erweiterte Sicherheitsfunktionen wie Token-Ablauf, Verwendungsverfolgung und Bindung an bestimmte Pfade oder IP-Adressen.
|
|
|
|
## Verwendung
|
|
|
|
### Token generieren und in Formularen verwenden
|
|
|
|
```php
|
|
// In einem Controller
|
|
public function showForm(): Response
|
|
{
|
|
// Token generieren
|
|
$csrfToken = $this->csrfTokenGenerator->generate();
|
|
|
|
// Token in der Session speichern
|
|
$this->session->set('csrf_token', $csrfToken->toString());
|
|
|
|
// Token im Formular einbinden
|
|
return $this->render('form.twig', [
|
|
'csrf_token' => $csrfToken->toString(),
|
|
]);
|
|
}
|
|
```
|
|
|
|
In der Formularvorlage:
|
|
|
|
```html
|
|
<form method="post" action="/submit">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<!-- Weitere Formularfelder -->
|
|
<button type="submit">Absenden</button>
|
|
</form>
|
|
```
|
|
|
|
### Token validieren
|
|
|
|
```php
|
|
// In einem Controller
|
|
public function handleForm(Request $request): Response
|
|
{
|
|
// Gespeichertes Token abrufen
|
|
$storedToken = CsrfToken::fromString($this->session->get('csrf_token'));
|
|
|
|
// Übermitteltes Token abrufen
|
|
$submittedToken = $request->getPostParam('csrf_token');
|
|
|
|
// Token vergleichen (zeitsichere Vergleichsmethode)
|
|
if (!$storedToken->equalsString($submittedToken)) {
|
|
throw new SecurityException('Ungültiges CSRF-Token');
|
|
}
|
|
|
|
// Token ist gültig, Formular verarbeiten
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Erweiterte Token-Validierung mit Metadaten
|
|
|
|
```php
|
|
// In einem Controller
|
|
public function showForm(): Response
|
|
{
|
|
// Token mit Metadaten generieren
|
|
$token = $this->csrfTokenGenerator->generate();
|
|
$tokenData = new CsrfTokenData(
|
|
$token,
|
|
$this->request->getClientIp(),
|
|
$this->request->path,
|
|
new \DateTimeImmutable('+1 hour')
|
|
);
|
|
|
|
// Token-Daten in der Session speichern
|
|
$this->session->set('csrf_token_data', $tokenData->toArray());
|
|
|
|
// Token im Formular einbinden
|
|
return $this->render('form.twig', [
|
|
'csrf_token' => $token->toString(),
|
|
]);
|
|
}
|
|
|
|
public function handleForm(Request $request): Response
|
|
{
|
|
// Token-Daten aus der Session abrufen
|
|
$tokenDataArray = $this->session->get('csrf_token_data');
|
|
$tokenData = CsrfTokenData::fromArray($tokenDataArray);
|
|
|
|
// Übermitteltes Token abrufen
|
|
$submittedToken = $request->getPostParam('csrf_token');
|
|
|
|
// Umfassende Validierung durchführen
|
|
if ($tokenData->isExpired($this->clock)) {
|
|
throw new SecurityException('CSRF-Token ist abgelaufen');
|
|
}
|
|
|
|
if ($tokenData->isUsed()) {
|
|
throw new SecurityException('CSRF-Token wurde bereits verwendet');
|
|
}
|
|
|
|
if ($tokenData->clientIp !== $request->getClientIp()) {
|
|
throw new SecurityException('CSRF-Token ist an eine andere IP-Adresse gebunden');
|
|
}
|
|
|
|
if (!$tokenData->token->equalsString($submittedToken)) {
|
|
throw new SecurityException('Ungültiges CSRF-Token');
|
|
}
|
|
|
|
// Token als verwendet markieren
|
|
$tokenData->markAsUsed();
|
|
$this->session->set('csrf_token_data', $tokenData->toArray());
|
|
|
|
// Token ist gültig, Formular verarbeiten
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## Integration mit dem Framework
|
|
|
|
### CSRF-Middleware
|
|
|
|
Das Framework bietet eine `CsrfProtectionMiddleware`, die automatisch CSRF-Schutz für alle POST, PUT, DELETE und PATCH-Anfragen implementiert:
|
|
|
|
```php
|
|
// In der Bootstrap-Datei oder Router-Konfiguration
|
|
$app->addMiddleware(CsrfProtectionMiddleware::class);
|
|
```
|
|
|
|
Die Middleware:
|
|
1. Generiert automatisch CSRF-Token für alle Formulare
|
|
2. Validiert Token für alle Anfragen, die den Zustand ändern
|
|
3. Wirft eine Exception oder gibt eine Fehlerantwort zurück, wenn die Validierung fehlschlägt
|
|
|
|
### Konfiguration
|
|
|
|
Die CSRF-Schutzfunktionen können in der Konfigurationsdatei angepasst werden:
|
|
|
|
```php
|
|
// config/security.php
|
|
return [
|
|
'csrf' => [
|
|
'enabled' => true,
|
|
'token_lifetime' => 3600, // Sekunden
|
|
'exclude_routes' => [
|
|
'/api/webhook',
|
|
'/api/external/callback',
|
|
],
|
|
'exclude_patterns' => [
|
|
'#^/api/public/#', // Regulärer Ausdruck für auszuschließende Routen
|
|
],
|
|
'header_name' => 'X-CSRF-Token', // Name des HTTP-Headers für API-Anfragen
|
|
'parameter_name' => 'csrf_token', // Name des Formularfelds
|
|
'strict_mode' => true, // Strikte Validierung (IP-Bindung, Einmalverwendung)
|
|
],
|
|
];
|
|
```
|
|
|
|
### Template-Integration
|
|
|
|
Das Framework bietet eine einfache Möglichkeit, CSRF-Token in Templates einzubinden:
|
|
|
|
```php
|
|
// In einem Controller
|
|
public function showForm(): Response
|
|
{
|
|
return $this->render('form.twig', [
|
|
'csrf_token' => $this->csrfProtection->getToken()->toString(),
|
|
]);
|
|
}
|
|
```
|
|
|
|
Mit der Template-Funktion:
|
|
|
|
```twig
|
|
{# In einer Twig-Vorlage #}
|
|
<form method="post" action="/submit">
|
|
{{ csrf_field() }}
|
|
<!-- Weitere Formularfelder -->
|
|
<button type="submit">Absenden</button>
|
|
</form>
|
|
```
|
|
|
|
Die `csrf_field()`-Funktion generiert automatisch ein verstecktes Formularfeld mit dem aktuellen CSRF-Token.
|
|
|
|
### API-Integration
|
|
|
|
Für API-Anfragen kann das CSRF-Token über einen HTTP-Header übermittelt werden:
|
|
|
|
```javascript
|
|
// JavaScript-Beispiel
|
|
fetch('/api/data', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
```
|
|
|
|
In der HTML-Vorlage:
|
|
|
|
```html
|
|
<meta name="csrf-token" content="{{ csrf_token }}">
|
|
```
|
|
|
|
## Erweiterte Funktionen
|
|
|
|
### Double Submit Cookie Pattern
|
|
|
|
Das Framework unterstützt das Double Submit Cookie Pattern für zusätzliche Sicherheit:
|
|
|
|
```php
|
|
// In einem Controller
|
|
public function showForm(): Response
|
|
{
|
|
$token = $this->csrfTokenGenerator->generate();
|
|
|
|
// Token in der Session speichern
|
|
$this->session->set('csrf_token', $token->toString());
|
|
|
|
// Token auch als Cookie setzen
|
|
$this->response->setCookie('csrf_token', $token->toString(), [
|
|
'httponly' => false, // JavaScript muss darauf zugreifen können
|
|
'samesite' => 'Strict',
|
|
'secure' => true,
|
|
'path' => '/',
|
|
]);
|
|
|
|
return $this->render('form.twig', [
|
|
'csrf_token' => $token->toString(),
|
|
]);
|
|
}
|
|
```
|
|
|
|
### Token-Rotation
|
|
|
|
Für zusätzliche Sicherheit können CSRF-Token nach jeder Anfrage rotiert werden:
|
|
|
|
```php
|
|
// In einem Controller
|
|
public function handleForm(Request $request): Response
|
|
{
|
|
// Token validieren
|
|
// ...
|
|
|
|
// Neues Token generieren
|
|
$newToken = $this->csrfTokenGenerator->generate();
|
|
$this->session->set('csrf_token', $newToken->toString());
|
|
|
|
// Antwort mit neuem Token
|
|
return $this->render('success.twig', [
|
|
'new_csrf_token' => $newToken->toString(),
|
|
]);
|
|
}
|
|
```
|
|
|
|
### Synchronizer Token Pattern
|
|
|
|
Das Framework implementiert das Synchronizer Token Pattern, bei dem für jede Sitzung ein eindeutiges Token generiert wird:
|
|
|
|
```php
|
|
// In der Session-Initialisierung
|
|
public function initializeSession(): void
|
|
{
|
|
if (!$this->session->has('csrf_token')) {
|
|
$token = $this->csrfTokenGenerator->generate();
|
|
$this->session->set('csrf_token', $token->toString());
|
|
}
|
|
}
|
|
```
|
|
|
|
## Fehlerbehebung
|
|
|
|
### Häufige Probleme
|
|
|
|
#### Token-Validierung schlägt fehl
|
|
|
|
Mögliche Ursachen:
|
|
- Das Token ist abgelaufen
|
|
- Das Token wurde bereits verwendet
|
|
- Das Token wurde nicht korrekt übermittelt
|
|
- Die Session ist abgelaufen oder wurde zurückgesetzt
|
|
|
|
Lösung:
|
|
- Überprüfen Sie die Session-Konfiguration
|
|
- Stellen Sie sicher, dass das Token korrekt im Formular eingebunden ist
|
|
- Erhöhen Sie die Token-Lebensdauer in der Konfiguration
|
|
|
|
#### CSRF-Schutz für bestimmte Routen deaktivieren
|
|
|
|
Wenn Sie den CSRF-Schutz für bestimmte Routen deaktivieren müssen (z.B. für Webhook-Callbacks):
|
|
|
|
```php
|
|
// config/security.php
|
|
return [
|
|
'csrf' => [
|
|
'exclude_routes' => [
|
|
'/api/webhook',
|
|
'/api/external/callback',
|
|
],
|
|
],
|
|
];
|
|
```
|
|
|
|
#### AJAX-Anfragen mit CSRF-Schutz
|
|
|
|
Für AJAX-Anfragen müssen Sie das CSRF-Token im Header oder als Formularfeld übermitteln:
|
|
|
|
```javascript
|
|
// JavaScript mit jQuery
|
|
$.ajaxSetup({
|
|
headers: {
|
|
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
|
}
|
|
});
|
|
|
|
// Oder mit Fetch API
|
|
fetch('/api/data', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
```
|
|
|
|
## Sicherheitsüberlegungen
|
|
|
|
### Best Practices
|
|
|
|
1. **Verwenden Sie HTTPS**: CSRF-Token sollten immer über eine verschlüsselte Verbindung übertragen werden.
|
|
2. **Kurze Token-Lebensdauer**: Setzen Sie die Token-Lebensdauer auf einen angemessenen Wert (z.B. 1-2 Stunden).
|
|
3. **Strikte SameSite-Cookie-Einstellungen**: Verwenden Sie `SameSite=Strict` oder `SameSite=Lax` für Session-Cookies.
|
|
4. **Token-Rotation**: Rotieren Sie Token nach jeder Anfrage, die den Zustand ändert.
|
|
5. **Validieren Sie alle Anfragen**: Stellen Sie sicher, dass alle Anfragen, die den Zustand ändern, ein gültiges CSRF-Token erfordern.
|
|
|
|
### Bekannte Einschränkungen
|
|
|
|
- CSRF-Schutz funktioniert nicht für Benutzer, die keine Cookies akzeptieren.
|
|
- Bei sehr langen Sitzungen kann die Sicherheit des CSRF-Tokens beeinträchtigt sein.
|
|
- CSRF-Schutz kann mit bestimmten Caching-Strategien in Konflikt geraten.
|
|
|
|
## Weiterführende Informationen
|
|
|
|
- [Security Features Übersicht](index.md)
|
|
- [Security Headers und CSP](security-headers.md)
|
|
- [Request Signing API](request-signing.md)
|
|
- [Sicherheits-Best-Practices](/guides/security-best-practices.md)
|