Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,477 @@
# Analytics Konfiguration
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der Analytics-Konfiguration korrekt dar.
## Übersicht
Das Analytics Framework bietet umfangreiche Konfigurationsmöglichkeiten, um das Verhalten und die Leistung an die Anforderungen Ihrer Anwendung anzupassen. Diese Dokumentation beschreibt die verfügbaren Konfigurationsoptionen und wie Sie diese anwenden können.
## Konfigurationsklasse
Die zentrale Klasse für die Analytics-Konfiguration ist `AnalyticsConfig`. Diese Klasse enthält alle Einstellungen, die das Verhalten des Analytics Frameworks steuern:
```php
use App\Framework\Analytics\AnalyticsConfig;
// Konfiguration erstellen
$config = new AnalyticsConfig(
$enabled = true, // Analytics aktivieren/deaktivieren
$samplingRate = 1.0, // Sampling-Rate (0.0 - 1.0)
$securityAnalyticsEnabled = true, // Sicherheits-Analytics aktivieren
$dataPath = '/var/www/html/storage/analytics', // Pfad für Datenspeicherung
$bufferSize = 1000, // Puffergröße für Ereignisse
$retentionDays = 365, // Aufbewahrungsdauer in Tagen
$trackPageViews = true, // Seitenaufrufe erfassen
$trackApiCalls = true, // API-Aufrufe erfassen
$trackUserActions = true, // Benutzeraktionen erfassen
$trackErrors = true, // Fehler erfassen
$trackPerformance = true // Leistungsmetriken erfassen
);
```
## Standardkonfiguration
Die `AnalyticsConfig`-Klasse bietet eine Standardkonfiguration, die für die meisten Anwendungen geeignet ist:
```php
// Standardkonfiguration verwenden
$config = AnalyticsConfig::default();
```
Die Standardkonfiguration aktiviert alle Features mit sinnvollen Standardwerten:
- Analytics aktiviert
- 100% Sampling (alle Daten werden erfasst)
- Sicherheits-Analytics aktiviert
- 1000 Ereignisse im Puffer
- 365 Tage Aufbewahrungsdauer
- Alle Tracking-Typen aktiviert
## Umgebungsvariablen
Die Konfiguration kann auch über Umgebungsvariablen angepasst werden:
```php
// In der AnalyticsConfig-Klasse
public static function default(): self
{
return new self(
enabled: filter_var($_ENV['ANALYTICS_ENABLED'] ?? true, FILTER_VALIDATE_BOOLEAN),
samplingRate: (float)($_ENV['ANALYTICS_SAMPLING_RATE'] ?? 1.0),
securityAnalyticsEnabled: filter_var($_ENV['SECURITY_ANALYTICS_ENABLED'] ?? true, FILTER_VALIDATE_BOOLEAN),
dataPath: $_ENV['ANALYTICS_DATA_PATH'] ?? '/var/www/html/storage/analytics',
bufferSize: (int)($_ENV['ANALYTICS_BUFFER_SIZE'] ?? 1000),
retentionDays: (int)($_ENV['ANALYTICS_RETENTION_DAYS'] ?? 365),
trackPageViews: filter_var($_ENV['ANALYTICS_TRACK_PAGE_VIEWS'] ?? true, FILTER_VALIDATE_BOOLEAN),
trackApiCalls: filter_var($_ENV['ANALYTICS_TRACK_API_CALLS'] ?? true, FILTER_VALIDATE_BOOLEAN),
trackUserActions: filter_var($_ENV['ANALYTICS_TRACK_USER_ACTIONS'] ?? true, FILTER_VALIDATE_BOOLEAN),
trackErrors: filter_var($_ENV['ANALYTICS_TRACK_ERRORS'] ?? true, FILTER_VALIDATE_BOOLEAN),
trackPerformance: filter_var($_ENV['ANALYTICS_TRACK_PERFORMANCE'] ?? true, FILTER_VALIDATE_BOOLEAN)
);
}
```
Beispiel für eine `.env`-Datei:
```
ANALYTICS_ENABLED=true
ANALYTICS_SAMPLING_RATE=0.1
SECURITY_ANALYTICS_ENABLED=true
ANALYTICS_DATA_PATH=/var/www/html/storage/analytics
ANALYTICS_BUFFER_SIZE=1000
ANALYTICS_RETENTION_DAYS=365
ANALYTICS_TRACK_PAGE_VIEWS=true
ANALYTICS_TRACK_API_CALLS=true
ANALYTICS_TRACK_USER_ACTIONS=true
ANALYTICS_TRACK_ERRORS=true
ANALYTICS_TRACK_PERFORMANCE=true
```
## Konfigurationsoptionen im Detail
### Grundlegende Optionen
#### enabled
Aktiviert oder deaktiviert das gesamte Analytics Framework:
```php
$config = new AnalyticsConfig(
enabled: true, // Analytics aktivieren
// ... weitere Optionen
);
// oder
$config = new AnalyticsConfig(
enabled: false, // Analytics deaktivieren
// ... weitere Optionen
);
```
Wenn deaktiviert, werden keine Daten erfasst, und alle Aufrufe an den `AnalyticsCollector` werden ignoriert.
#### samplingRate
Definiert den Prozentsatz der Daten, die erfasst werden sollen:
```php
$config = new AnalyticsConfig(
enabled: true,
samplingRate: 1.0, // 100% der Daten erfassen
// ... weitere Optionen
);
// oder
$config = new AnalyticsConfig(
enabled: true,
samplingRate: 0.1, // 10% der Daten erfassen
// ... weitere Optionen
);
```
Die Sampling-Rate ist nützlich, um die Datenmenge und die Serverbelastung bei hohem Datenaufkommen zu reduzieren. Ein Wert von 0.1 bedeutet, dass nur 10% aller Ereignisse erfasst werden.
#### securityAnalyticsEnabled
Aktiviert oder deaktiviert die Erfassung von Sicherheitsereignissen:
```php
$config = new AnalyticsConfig(
enabled: true,
samplingRate: 1.0,
securityAnalyticsEnabled: true, // Sicherheits-Analytics aktivieren
// ... weitere Optionen
);
```
Wenn aktiviert, werden Sicherheitsereignisse (wie Anmeldeversuche, CSRF-Angriffe, WAF-Blockierungen) erfasst und analysiert.
### Speicheroptionen
#### dataPath
Definiert den Pfad, in dem Analytics-Daten gespeichert werden:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
dataPath: '/var/www/html/storage/analytics',
// ... weitere Optionen
);
```
Stellen Sie sicher, dass dieser Pfad existiert und für die Anwendung beschreibbar ist.
#### bufferSize
Definiert die Anzahl der Ereignisse, die im Speicher gepuffert werden, bevor sie auf die Festplatte geschrieben werden:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
bufferSize: 1000, // 1000 Ereignisse puffern
// ... weitere Optionen
);
```
Ein größerer Puffer reduziert die Anzahl der Schreibvorgänge, erhöht aber den Speicherverbrauch und das Risiko von Datenverlust bei einem Absturz.
#### retentionDays
Definiert die Anzahl der Tage, für die Analytics-Daten aufbewahrt werden:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
retentionDays: 365, // Daten für 1 Jahr aufbewahren
// ... weitere Optionen
);
```
Ältere Daten werden automatisch gelöscht, um Speicherplatz zu sparen.
### Tracking-Optionen
#### trackPageViews
Aktiviert oder deaktiviert die Erfassung von Seitenaufrufen:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
trackPageViews: true, // Seitenaufrufe erfassen
// ... weitere Optionen
);
```
#### trackApiCalls
Aktiviert oder deaktiviert die Erfassung von API-Aufrufen:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
trackApiCalls: true, // API-Aufrufe erfassen
// ... weitere Optionen
);
```
#### trackUserActions
Aktiviert oder deaktiviert die Erfassung von Benutzeraktionen:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
trackUserActions: true, // Benutzeraktionen erfassen
// ... weitere Optionen
);
```
#### trackErrors
Aktiviert oder deaktiviert die Erfassung von Fehlern:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
trackErrors: true, // Fehler erfassen
// ... weitere Optionen
);
```
#### trackPerformance
Aktiviert oder deaktiviert die Erfassung von Leistungsmetriken:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
trackPerformance: true, // Leistungsmetriken erfassen
);
```
## Konfiguration in der Anwendung
### Konfigurationsdatei
Die Analytics-Konfiguration kann in einer dedizierten Konfigurationsdatei definiert werden:
```php
// config/analytics.php
return [
'enabled' => true,
'sampling_rate' => 1.0,
'security_analytics_enabled' => true,
'data_path' => '/var/www/html/storage/analytics',
'buffer_size' => 1000,
'retention_days' => 365,
'track_page_views' => true,
'track_api_calls' => true,
'track_user_actions' => true,
'track_errors' => true,
'track_performance' => true,
];
```
### Dependency Injection
Die Konfiguration kann über Dependency Injection in die Anwendung eingebunden werden:
```php
// In einem Service Provider oder Container-Konfiguration
$container->set(AnalyticsConfig::class, function () {
return new AnalyticsConfig(
enabled: true,
samplingRate: 0.1,
// ... weitere Optionen
);
});
```
### Laufzeitkonfiguration
Die Konfiguration kann zur Laufzeit angepasst werden:
```php
// Analytics-Konfiguration aktualisieren
$newConfig = new AnalyticsConfig(
enabled: true,
samplingRate: 0.5, // Sampling-Rate anpassen
// ... weitere Optionen
);
// Konfiguration aktualisieren
$analyticsManager->updateConfig($newConfig);
```
## Umgebungsspezifische Konfiguration
Es ist oft sinnvoll, unterschiedliche Konfigurationen für verschiedene Umgebungen zu verwenden:
### Produktionsumgebung
```php
// Produktionskonfiguration
$config = new AnalyticsConfig(
enabled: true,
samplingRate: 0.1, // Reduzierte Sampling-Rate für hohe Lasten
securityAnalyticsEnabled: true,
dataPath: '/var/www/html/storage/analytics',
bufferSize: 5000, // Größerer Puffer für bessere Leistung
retentionDays: 365,
trackPageViews: true,
trackApiCalls: true,
trackUserActions: true,
trackErrors: true,
trackPerformance: true
);
```
### Entwicklungsumgebung
```php
// Entwicklungskonfiguration
$config = new AnalyticsConfig(
enabled: true,
samplingRate: 1.0, // Alle Daten erfassen für einfachere Fehlersuche
securityAnalyticsEnabled: true,
dataPath: '/var/www/html/storage/analytics',
bufferSize: 100, // Kleinerer Puffer für sofortiges Feedback
retentionDays: 30, // Kürzere Aufbewahrungsdauer
trackPageViews: true,
trackApiCalls: true,
trackUserActions: true,
trackErrors: true,
trackPerformance: true
);
```
### Testumgebung
```php
// Testkonfiguration
$config = new AnalyticsConfig(
enabled: false, // Deaktiviert für Tests
samplingRate: 1.0,
securityAnalyticsEnabled: false,
dataPath: '/tmp/analytics',
bufferSize: 10,
retentionDays: 1,
trackPageViews: false,
trackApiCalls: false,
trackUserActions: false,
trackErrors: false,
trackPerformance: false
);
```
## Leistungsoptimierung
### Sampling für hohe Lasten
Bei hohem Datenaufkommen kann die Sampling-Rate reduziert werden, um die Serverbelastung zu verringern:
```php
$config = new AnalyticsConfig(
enabled: true,
samplingRate: 0.01, // Nur 1% der Daten erfassen
// ... weitere Optionen
);
```
### Pufferoptimierung
Die Puffergröße kann angepasst werden, um die Anzahl der Schreibvorgänge zu optimieren:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
bufferSize: 10000, // Größerer Puffer für weniger Schreibvorgänge
// ... weitere Optionen
);
```
### Selektives Tracking
Nicht alle Tracking-Typen müssen aktiviert sein. Deaktivieren Sie die Typen, die Sie nicht benötigen:
```php
$config = new AnalyticsConfig(
// ... vorherige Optionen
trackPageViews: true,
trackApiCalls: true,
trackUserActions: false, // Benutzeraktionen deaktivieren
trackErrors: true,
trackPerformance: false // Leistungsmetriken deaktivieren
);
```
## Fehlerbehebung
### Häufige Probleme
#### Keine Daten werden erfasst
Mögliche Ursachen:
- Analytics ist deaktiviert (`enabled = false`)
- Sampling-Rate ist zu niedrig
- Fehler beim Speichern der Daten
Lösung:
- Überprüfen Sie die Konfiguration
- Erhöhen Sie die Sampling-Rate
- Überprüfen Sie die Logs auf Fehler
- Stellen Sie sicher, dass der Datenpfad existiert und beschreibbar ist
```php
// Überprüfen Sie die Konfiguration
var_dump($analyticsConfig);
// Erhöhen Sie die Sampling-Rate
$newConfig = new AnalyticsConfig(
enabled: true,
samplingRate: 1.0, // 100% der Daten erfassen
// ... weitere Optionen
);
// Überprüfen Sie die Berechtigungen des Datenpfads
if (!is_writable($analyticsConfig->dataPath)) {
echo "Datenpfad ist nicht beschreibbar: " . $analyticsConfig->dataPath;
}
```
#### Hohe Serverbelastung
Mögliche Ursachen:
- Zu viele Ereignisse werden erfasst
- Puffergröße ist zu klein
- Synchrone Verarbeitung bei hohem Datenaufkommen
Lösung:
- Reduzieren Sie die Sampling-Rate
- Erhöhen Sie die Puffergröße
- Verwenden Sie asynchrone Verarbeitung
```php
// Reduzieren Sie die Sampling-Rate
$newConfig = new AnalyticsConfig(
enabled: true,
samplingRate: 0.1, // Nur 10% der Daten erfassen
// ... weitere Optionen
);
// Erhöhen Sie die Puffergröße
$newConfig = new AnalyticsConfig(
// ... vorherige Optionen
bufferSize: 10000, // Größerer Puffer
// ... weitere Optionen
);
```
## Weiterführende Informationen
- [Analytics Framework Übersicht](index.md)
- [Analytics Beispiele](examples.md)
- [Performance-Monitoring](/components/performance/index.md)

View File

@@ -0,0 +1,760 @@
# Analytics Framework Beispiele
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung des Analytics Frameworks korrekt dar.
## Übersicht
Diese Dokumentation enthält praktische Beispiele für die Verwendung des Analytics Frameworks in verschiedenen Szenarien. Die Beispiele zeigen, wie Sie Analytics-Daten erfassen, analysieren und in Ihre Anwendung integrieren können.
## Grundlegende Verwendung
### Benutzeraktionen erfassen
```php
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\AnalyticsCategory;
class UserController
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector
) {}
public function handleLogin(Request $request): Response
{
// Login-Logik
$success = $this->authService->login($request->getParam('email'), $request->getParam('password'));
// Login-Aktion erfassen
$this->analyticsCollector->trackAction(
'user_login',
AnalyticsCategory::USER,
[
'success' => $success,
'email_domain' => explode('@', $request->getParam('email'))[1] ?? 'unknown',
'ip' => $request->getClientIp(),
'user_agent' => $request->getUserAgent()
]
);
// Antwort zurückgeben
return $success
? $this->redirect('/dashboard')
: $this->renderForm('login', ['error' => 'Ungültige Anmeldedaten']);
}
}
```
### Seitenaufrufe erfassen
```php
use App\Framework\Analytics\AnalyticsCollector;
class ProductController
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector,
private readonly ProductRepository $productRepository
) {}
public function showProduct(Request $request, string $productId): Response
{
$product = $this->productRepository->find($productId);
if (!$product) {
return $this->notFound();
}
// Seitenaufruf erfassen
$this->analyticsCollector->trackPageView(
"/products/{$productId}",
[
'product_id' => $productId,
'product_name' => $product->name,
'product_category' => $product->category,
'referrer' => $request->getHeader('Referer'),
'user_id' => $this->session->get('user_id')
]
);
return $this->render('product', ['product' => $product]);
}
}
```
### Fehler erfassen
```php
use App\Framework\Analytics\AnalyticsCollector;
class ErrorHandler
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector,
private readonly Logger $logger
) {}
public function handleException(\Throwable $exception): Response
{
// Fehler loggen
$this->logger->error('Unbehandelte Ausnahme: ' . $exception->getMessage(), [
'exception' => $exception
]);
// Fehler für Analytics erfassen
$this->analyticsCollector->trackError(
get_class($exception),
[
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
'request_uri' => $_SERVER['REQUEST_URI'] ?? null,
'user_id' => $this->session->get('user_id')
]
);
// Fehlerseite anzeigen
return new Response(500, [], 'Ein Fehler ist aufgetreten');
}
}
```
### Geschäftsereignisse erfassen
```php
use App\Framework\Analytics\AnalyticsCollector;
class OrderService
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector,
private readonly OrderRepository $orderRepository
) {}
public function placeOrder(Order $order): bool
{
// Bestellung speichern
$success = $this->orderRepository->save($order);
if ($success) {
// Geschäftsereignis erfassen
$this->analyticsCollector->trackBusinessEvent(
'order_placed',
[
'order_id' => $order->id,
'user_id' => $order->userId,
'total_amount' => $order->totalAmount,
'items_count' => count($order->items),
'payment_method' => $order->paymentMethod,
'shipping_country' => $order->shippingAddress->country
]
);
}
return $success;
}
}
```
### API-Aufrufe erfassen
```php
use App\Framework\Analytics\AnalyticsCollector;
class ApiClient
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector,
private readonly HttpClient $httpClient
) {}
public function sendRequest(string $endpoint, string $method, array $data = []): Response
{
$startTime = microtime(true);
try {
$response = $this->httpClient->request($method, $endpoint, $data);
$statusCode = $response->getStatusCode();
$success = $statusCode >= 200 && $statusCode < 300;
} catch (\Exception $e) {
$statusCode = 0;
$success = false;
}
$duration = (microtime(true) - $startTime) * 1000; // in ms
// API-Aufruf erfassen
$this->analyticsCollector->trackApiCall(
$endpoint,
$method,
$statusCode,
[
'duration' => $duration,
'success' => $success,
'data_size' => strlen(json_encode($data)),
'user_id' => $this->session->get('user_id')
]
);
return $response;
}
}
```
## Fortgeschrittene Verwendung
### Automatische Erfassung mit Middleware
Die `AnalyticsMiddleware` erfasst automatisch Seitenaufrufe und API-Aufrufe:
```php
use App\Framework\Analytics\Middleware\AnalyticsMiddleware;
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(AnalyticsMiddleware::class);
```
Anpassung der Middleware für bestimmte Routen:
```php
use App\Framework\Analytics\Middleware\AnalyticsMiddleware;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Next;
class CustomAnalyticsMiddleware extends AnalyticsMiddleware
{
public function __invoke(MiddlewareContext $context, Next $next): MiddlewareContext
{
$request = $context->request;
// Bestimmte Routen ausschließen
if (str_starts_with($request->path, '/api/health') || str_starts_with($request->path, '/static/')) {
return $next($context);
}
// Zusätzliche Daten für bestimmte Routen hinzufügen
$additionalData = [];
if (str_starts_with($request->path, '/products/')) {
$productId = substr($request->path, 10);
$additionalData['product_id'] = $productId;
}
// Standardverhalten mit zusätzlichen Daten
return parent::__invoke($context, $next, $additionalData);
}
}
```
### Sicherheitsereignisse erfassen
Integration mit dem Security-System:
```php
use App\Framework\Analytics\Listeners\SecurityAnalyticsListener;
use App\Framework\Analytics\Bridges\SecurityEventBridge;
use App\Framework\Security\Events\LoginAttemptEvent;
// In der Bootstrap-Datei
$securityEventBridge = SecurityEventBridge::create(
$container->get(SecurityAnalyticsListener::class),
$config->securityAnalyticsEnabled
);
$eventDispatcher->addListener(LoginAttemptEvent::class, [$securityEventBridge, 'handleSecurityEvent']);
```
Benutzerdefinierter Security-Event-Listener:
```php
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\AnalyticsCategory;
use App\Framework\Security\Events\LoginAttemptEvent;
class LoginAnalyticsListener
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector
) {}
public function onLoginAttempt(LoginAttemptEvent $event): void
{
$this->analyticsCollector->trackAction(
'login_attempt',
AnalyticsCategory::SECURITY,
[
'username' => $event->getUsername(),
'success' => $event->isSuccessful(),
'ip' => $event->getIp(),
'user_agent' => $event->getUserAgent(),
'timestamp' => $event->getTimestamp()->format('Y-m-d H:i:s')
]
);
}
}
```
### Asynchrone Datenerfassung
Für maximale Leistung kann die Analytics-Datenerfassung asynchron erfolgen:
```php
use App\Framework\Analytics\AsyncAnalyticsCollector;
use App\Framework\Queue\QueueInterface;
class AsyncAnalyticsService
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector,
private readonly QueueInterface $queue
) {}
public function initialize(): void
{
// Asynchronen Collector erstellen
$asyncCollector = new AsyncAnalyticsCollector(
$this->analyticsCollector,
$this->queue
);
// Asynchronen Collector registrieren
$this->container->set(AnalyticsCollector::class, $asyncCollector);
}
}
```
### Benutzerdefinierte Ereignistypen
Sie können benutzerdefinierte Ereignistypen für spezifische Anforderungen erstellen:
```php
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\AnalyticsCategory;
class CustomAnalyticsService
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector
) {}
public function trackProductView(string $productId, string $productName, string $category): void
{
$this->analyticsCollector->trackAction(
'product_view',
AnalyticsCategory::BUSINESS,
[
'product_id' => $productId,
'product_name' => $productName,
'category' => $category,
'timestamp' => time(),
'user_id' => $this->session->get('user_id')
]
);
}
public function trackAddToCart(string $productId, int $quantity, float $price): void
{
$this->analyticsCollector->trackAction(
'add_to_cart',
AnalyticsCategory::BUSINESS,
[
'product_id' => $productId,
'quantity' => $quantity,
'price' => $price,
'total' => $quantity * $price,
'timestamp' => time(),
'user_id' => $this->session->get('user_id')
]
);
}
}
```
## Datenanalyse
### Rohdaten abfragen
```php
use App\Framework\Analytics\Storage\AnalyticsStorage;
class AnalyticsReportService
{
public function __construct(
private readonly AnalyticsStorage $analyticsStorage
) {}
public function getPageViewsReport(\DateTimeInterface $from, \DateTimeInterface $to): array
{
// Rohdaten für Seitenaufrufe abrufen
$pageViews = $this->analyticsStorage->getRawData(
'page_view',
$from,
$to
);
// Daten nach Seite gruppieren
$pageViewsByPath = [];
foreach ($pageViews as $pageView) {
$path = $pageView['path'] ?? 'unknown';
if (!isset($pageViewsByPath[$path])) {
$pageViewsByPath[$path] = 0;
}
$pageViewsByPath[$path]++;
}
// Nach Anzahl sortieren
arsort($pageViewsByPath);
return $pageViewsByPath;
}
public function getUserActivityReport(string $userId, \DateTimeInterface $from, \DateTimeInterface $to): array
{
// Alle Aktionen für einen bestimmten Benutzer abrufen
$actions = $this->analyticsStorage->getRawData(
'user_action',
$from,
$to,
['user_id' => $userId]
);
// Nach Zeitstempel sortieren
usort($actions, function ($a, $b) {
return $a['timestamp'] <=> $b['timestamp'];
});
return $actions;
}
}
```
### Aggregierte Daten abfragen
```php
use App\Framework\Analytics\Storage\AnalyticsStorage;
class PerformanceReportService
{
public function __construct(
private readonly AnalyticsStorage $analyticsStorage
) {}
public function getApiPerformanceReport(\DateTimeInterface $from, \DateTimeInterface $to): array
{
// Aggregierte Daten für API-Aufrufe abrufen
$apiCallStats = $this->analyticsStorage->getAggregatedData(
'analytics_api_calls_total',
$from,
$to
);
// Durchschnittliche Antwortzeit pro Endpunkt berechnen
$responseTimeByEndpoint = [];
foreach ($apiCallStats as $stat) {
$endpoint = $stat['tags']['path'] ?? 'unknown';
$method = $stat['tags']['method'] ?? 'unknown';
$key = "{$method} {$endpoint}";
if (!isset($responseTimeByEndpoint[$key])) {
$responseTimeByEndpoint[$key] = [
'count' => 0,
'total_time' => 0,
'min_time' => PHP_FLOAT_MAX,
'max_time' => 0,
];
}
$duration = $stat['tags']['duration'] ?? 0;
$responseTimeByEndpoint[$key]['count']++;
$responseTimeByEndpoint[$key]['total_time'] += $duration;
$responseTimeByEndpoint[$key]['min_time'] = min($responseTimeByEndpoint[$key]['min_time'], $duration);
$responseTimeByEndpoint[$key]['max_time'] = max($responseTimeByEndpoint[$key]['max_time'], $duration);
}
// Durchschnitt berechnen
foreach ($responseTimeByEndpoint as $key => $data) {
$responseTimeByEndpoint[$key]['avg_time'] = $data['total_time'] / $data['count'];
}
// Nach durchschnittlicher Antwortzeit sortieren
uasort($responseTimeByEndpoint, function ($a, $b) {
return $b['avg_time'] <=> $a['avg_time'];
});
return $responseTimeByEndpoint;
}
}
```
### Dashboard-Integration
```php
use App\Framework\Analytics\Dashboard\AnalyticsDashboard;
class AdminController
{
public function __construct(
private readonly AnalyticsDashboard $dashboard,
private readonly TemplateEngine $templateEngine
) {}
public function showDashboard(Request $request): Response
{
// Zeitraum aus Anfrage oder Standardwerte
$from = new \DateTimeImmutable($request->getQueryParam('from', '-7 days'));
$to = new \DateTimeImmutable($request->getQueryParam('to', 'now'));
// Dashboard-Daten abrufen
$stats = $this->dashboard->getStats($from, $to);
// Spezifische Statistiken abrufen
$pageViewStats = $this->dashboard->getPageViewStats($from, $to);
$errorStats = $this->dashboard->getErrorStats($from, $to);
$performanceStats = $this->dashboard->getPerformanceStats($from, $to);
$businessStats = $this->dashboard->getBusinessStats($from, $to);
// Dashboard rendern
return $this->templateEngine->render('admin/dashboard', [
'stats' => $stats,
'pageViewStats' => $pageViewStats,
'errorStats' => $errorStats,
'performanceStats' => $performanceStats,
'businessStats' => $businessStats,
'from' => $from,
'to' => $to
]);
}
}
```
## Datenexport
### CSV-Export
```php
use App\Framework\Analytics\Export\AnalyticsExporter;
class ExportController
{
public function __construct(
private readonly AnalyticsExporter $exporter
) {}
public function exportPageViews(Request $request): Response
{
// Zeitraum aus Anfrage
$from = new \DateTimeImmutable($request->getQueryParam('from', '-7 days'));
$to = new \DateTimeImmutable($request->getQueryParam('to', 'now'));
// Daten exportieren
$csvData = $this->exporter->exportToCsv(
'page_view',
$from,
$to
);
// CSV-Datei zurückgeben
return new Response(
200,
[
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="page_views.csv"'
],
$csvData
);
}
}
```
### JSON-Export
```php
use App\Framework\Analytics\Export\AnalyticsExporter;
class ApiController
{
public function __construct(
private readonly AnalyticsExporter $exporter
) {}
public function getAnalyticsData(Request $request): Response
{
// Parameter aus Anfrage
$eventType = $request->getQueryParam('type', 'page_view');
$from = new \DateTimeImmutable($request->getQueryParam('from', '-7 days'));
$to = new \DateTimeImmutable($request->getQueryParam('to', 'now'));
// Daten exportieren
$data = $this->exporter->exportToArray(
$eventType,
$from,
$to
);
// JSON-Daten zurückgeben
return new Response(
200,
['Content-Type' => 'application/json'],
json_encode($data)
);
}
}
```
## Integration mit anderen Systemen
### Event-System-Integration
```php
use App\Framework\Analytics\EventSubscribers\AnalyticsEventSubscriber;
use App\Framework\EventDispatcher\EventDispatcherInterface;
class AnalyticsBootstrap
{
public function initialize(EventDispatcherInterface $eventDispatcher): void
{
// Analytics-Event-Subscriber registrieren
$eventDispatcher->addSubscriber(new AnalyticsEventSubscriber(
$this->container->get(AnalyticsCollector::class)
));
}
}
```
### Externe Analytics-Integration
```php
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\AnalyticsCategory;
class ExternalAnalyticsService
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector,
private readonly ExternalAnalyticsClient $externalClient
) {}
public function trackEvent(string $eventName, array $properties): void
{
// Intern erfassen
$this->analyticsCollector->trackAction(
$eventName,
AnalyticsCategory::BUSINESS,
$properties
);
// An externes System senden
$this->externalClient->trackEvent($eventName, $properties);
}
public function syncData(\DateTimeInterface $from, \DateTimeInterface $to): void
{
// Daten aus internem Analytics abrufen
$internalData = $this->analyticsStorage->getRawData('*', $from, $to);
// Daten an externes System senden
$this->externalClient->batchImport($internalData);
}
}
```
## Fehlerbehebung
### Debugging der Datenerfassung
```php
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\AnalyticsCategory;
class DebugAnalyticsCollector extends AnalyticsCollector
{
public function trackAction(string $action, AnalyticsCategory $category, array $data = []): void
{
// Debug-Ausgabe
echo "Tracking action: {$action}, category: {$category->value}\n";
echo "Data: " . json_encode($data, JSON_PRETTY_PRINT) . "\n";
// Original-Methode aufrufen
parent::trackAction($action, $category, $data);
}
public function trackPageView(string $path, array $data = []): void
{
// Debug-Ausgabe
echo "Tracking page view: {$path}\n";
echo "Data: " . json_encode($data, JSON_PRETTY_PRINT) . "\n";
// Original-Methode aufrufen
parent::trackPageView($path, $data);
}
}
```
### Überprüfung der Datenspeicherung
```php
use App\Framework\Analytics\Storage\AnalyticsStorage;
class AnalyticsDebugService
{
public function __construct(
private readonly AnalyticsStorage $analyticsStorage,
private readonly string $dataPath
) {}
public function checkStorage(): array
{
$result = [
'status' => 'ok',
'messages' => [],
'files' => []
];
// Prüfen, ob der Datenpfad existiert
if (!is_dir($this->dataPath)) {
$result['status'] = 'error';
$result['messages'][] = "Datenpfad existiert nicht: {$this->dataPath}";
return $result;
}
// Prüfen, ob der Datenpfad beschreibbar ist
if (!is_writable($this->dataPath)) {
$result['status'] = 'error';
$result['messages'][] = "Datenpfad ist nicht beschreibbar: {$this->dataPath}";
return $result;
}
// Dateien im Datenpfad auflisten
$files = glob($this->dataPath . '/*.json');
foreach ($files as $file) {
$size = filesize($file);
$modified = date('Y-m-d H:i:s', filemtime($file));
$result['files'][] = [
'name' => basename($file),
'size' => $size,
'modified' => $modified
];
}
// Testdaten speichern
try {
$this->analyticsStorage->storeRawData('debug_test', [
'timestamp' => time(),
'message' => 'Debug test'
]);
$result['messages'][] = "Testdaten erfolgreich gespeichert";
} catch (\Exception $e) {
$result['status'] = 'error';
$result['messages'][] = "Fehler beim Speichern von Testdaten: " . $e->getMessage();
}
return $result;
}
}
```
## Weiterführende Informationen
- [Analytics Framework Übersicht](index.md)
- [Analytics Konfiguration](configuration.md)
- [Performance-Monitoring](/components/performance/index.md)

View File

@@ -0,0 +1,507 @@
# Analytics Framework
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung des Analytics Frameworks korrekt dar.
## Übersicht
Das Analytics Framework ist ein leistungsstarkes System zur Erfassung, Speicherung und Analyse von Daten über die Nutzung und Leistung Ihrer Anwendung. Es ist vollständig in das Framework integriert und ermöglicht die Erfassung verschiedener Arten von Ereignissen, darunter Seitenaufrufe, Benutzeraktionen, API-Aufrufe, Fehler und Geschäftsereignisse.
Das Framework basiert auf dem Performance-System und bietet eine flexible, erweiterbare Architektur für verschiedene Analyseanforderungen, ohne auf externe Dienste angewiesen zu sein.
## Hauptkomponenten
### AnalyticsCollector
Die zentrale Klasse für die Erfassung von Analytics-Daten:
```php
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\AnalyticsCategory;
// Benutzeraktion erfassen
$analyticsCollector->trackAction(
'button_click',
AnalyticsCategory::USER,
['button_id' => 'submit', 'page' => '/checkout']
);
// Seitenaufruf erfassen
$analyticsCollector->trackPageView(
'/products',
['referrer' => '/home', 'user_id' => $userId]
);
// Fehler erfassen
$analyticsCollector->trackError(
'validation_error',
['field' => 'email', 'message' => 'Invalid email format']
);
// Geschäftsereignis erfassen
$analyticsCollector->trackBusinessEvent(
'order_completed',
['order_id' => '12345', 'amount' => 99.99, 'items' => 3]
);
// API-Aufruf erfassen
$analyticsCollector->trackApiCall(
'/api/products',
'GET',
200,
['duration' => 45, 'cache_hit' => true]
);
```
### AnalyticsCategory
Ein Enum, das die verschiedenen Kategorien von Analytics-Daten definiert:
```php
use App\Framework\Analytics\AnalyticsCategory;
// Verfügbare Kategorien
$category = AnalyticsCategory::USER; // Benutzeraktionen
$category = AnalyticsCategory::SYSTEM; // Systemereignisse
$category = AnalyticsCategory::BUSINESS; // Geschäftsereignisse
$category = AnalyticsCategory::PERFORMANCE; // Leistungsmetriken
$category = AnalyticsCategory::SECURITY; // Sicherheitsereignisse
```
### AnalyticsStorage
Das Interface für die Speicherung von Analytics-Daten:
```php
use App\Framework\Analytics\Storage\AnalyticsStorage;
interface AnalyticsStorage
{
// Rohdaten speichern
public function storeRawData(string $eventType, array $data): void;
// Aggregierte Daten speichern
public function storeAggregatedData(string $metricName, float $value, array $tags = []): void;
// Rohdaten abrufen
public function getRawData(string $eventType, \DateTimeInterface $from, \DateTimeInterface $to): array;
// Aggregierte Daten abrufen
public function getAggregatedData(string $metricName, \DateTimeInterface $from, \DateTimeInterface $to, array $tags = []): array;
}
```
Das Framework bietet eine leistungsoptimierte Implementierung:
```php
use App\Framework\Analytics\Storage\PerformanceBasedAnalyticsStorage;
// Leistungsoptimierte Speicherung
$storage = new PerformanceBasedAnalyticsStorage(
$filesystem,
$performanceSystem,
$clock,
$config
);
```
### AnalyticsMiddleware
Eine Middleware, die automatisch Analytics-Daten für HTTP-Anfragen erfasst:
```php
use App\Framework\Analytics\Middleware\AnalyticsMiddleware;
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(AnalyticsMiddleware::class);
```
Die Middleware erfasst automatisch:
- Seitenaufrufe
- API-Aufrufe
- Leistungsmetriken (Antwortzeit, Speicherverbrauch)
- HTTP-Statuscodes
- Benutzeragenten und Referrer
### SecurityAnalyticsListener
Ein Listener, der Sicherheitsereignisse in Analytics-Daten umwandelt:
```php
use App\Framework\Analytics\Listeners\SecurityAnalyticsListener;
use App\Framework\Analytics\Bridges\SecurityEventBridge;
// In der Bootstrap-Datei
$securityEventBridge = SecurityEventBridge::create(
$container->get(SecurityAnalyticsListener::class),
$config->securityAnalyticsEnabled
);
$eventDispatcher->addListener(SecurityEventInterface::class, [$securityEventBridge, 'handleSecurityEvent']);
```
## Datenerfassung
### Benutzeraktionen
Benutzeraktionen repräsentieren Interaktionen des Benutzers mit der Anwendung:
```php
$analyticsCollector->trackAction(
'button_click', // Aktionsname
AnalyticsCategory::USER, // Kategorie
[ // Zusätzliche Daten
'button_id' => 'submit',
'page' => '/checkout',
'user_id' => $userId
]
);
```
### Seitenaufrufe
Seitenaufrufe repräsentieren den Besuch einer Seite durch einen Benutzer:
```php
$analyticsCollector->trackPageView(
'/products', // Pfad
[ // Zusätzliche Daten
'referrer' => '/home',
'user_id' => $userId,
'device_type' => 'mobile'
]
);
```
### Fehler
Fehler repräsentieren Probleme, die während der Ausführung der Anwendung auftreten:
```php
$analyticsCollector->trackError(
'validation_error', // Fehlertyp
[ // Zusätzliche Daten
'field' => 'email',
'message' => 'Invalid email format',
'user_id' => $userId
]
);
// Oder mit einer Exception
try {
// Code, der eine Exception werfen könnte
} catch (\Exception $e) {
$analyticsCollector->trackException($e);
}
```
### Geschäftsereignisse
Geschäftsereignisse repräsentieren wichtige Ereignisse aus geschäftlicher Sicht:
```php
$analyticsCollector->trackBusinessEvent(
'order_completed', // Ereignisname
[ // Zusätzliche Daten
'order_id' => '12345',
'amount' => 99.99,
'items' => 3,
'user_id' => $userId
]
);
```
### API-Aufrufe
API-Aufrufe repräsentieren Anfragen an die API der Anwendung:
```php
$analyticsCollector->trackApiCall(
'/api/products', // Pfad
'GET', // Methode
200, // Statuscode
[ // Zusätzliche Daten
'duration' => 45,
'cache_hit' => true,
'user_id' => $userId
]
);
```
## Konfiguration
### AnalyticsConfig
Die `AnalyticsConfig`-Klasse enthält die Konfiguration für das Analytics Framework:
```php
use App\Framework\Analytics\AnalyticsConfig;
// Standardkonfiguration
$config = AnalyticsConfig::default();
// Benutzerdefinierte Konfiguration
$config = new AnalyticsConfig(
$enabled = true,
$samplingRate = 1.0,
$securityAnalyticsEnabled = true,
$dataPath = '/var/www/html/storage/analytics',
$bufferSize = 1000,
$retentionDays = 365,
$trackPageViews = true,
$trackApiCalls = true,
$trackUserActions = true,
$trackErrors = true,
$trackPerformance = true
);
```
### Umgebungsvariablen
Die Konfiguration kann auch über Umgebungsvariablen angepasst werden:
```
ANALYTICS_ENABLED=true
ANALYTICS_SAMPLING_RATE=0.1
SECURITY_ANALYTICS_ENABLED=true
ANALYTICS_DATA_PATH=/var/www/html/storage/analytics
ANALYTICS_BUFFER_SIZE=1000
ANALYTICS_RETENTION_DAYS=365
ANALYTICS_TRACK_PAGE_VIEWS=true
ANALYTICS_TRACK_API_CALLS=true
ANALYTICS_TRACK_USER_ACTIONS=true
ANALYTICS_TRACK_ERRORS=true
ANALYTICS_TRACK_PERFORMANCE=true
```
## Datenanalyse
### Rohdaten abrufen
```php
// Rohdaten für Seitenaufrufe abrufen
$pageViews = $analyticsStorage->getRawData(
'page_view',
new \DateTimeImmutable('-7 days'),
new \DateTimeImmutable()
);
// Rohdaten für Geschäftsereignisse abrufen
$orders = $analyticsStorage->getRawData(
'business_event_order_completed',
new \DateTimeImmutable('-30 days'),
new \DateTimeImmutable()
);
```
### Aggregierte Daten abrufen
```php
// Aggregierte Daten für Seitenaufrufe abrufen
$pageViewStats = $analyticsStorage->getAggregatedData(
'analytics_page_views_total',
new \DateTimeImmutable('-7 days'),
new \DateTimeImmutable(),
['path' => '/products']
);
// Aggregierte Daten für API-Aufrufe abrufen
$apiCallStats = $analyticsStorage->getAggregatedData(
'analytics_api_calls_total',
new \DateTimeImmutable('-7 days'),
new \DateTimeImmutable(),
['path' => '/api/products', 'method' => 'GET']
);
```
### Dashboard
Das Analytics-Dashboard bietet eine visuelle Darstellung der erfassten Daten:
```php
use App\Framework\Analytics\Dashboard\AnalyticsDashboard;
// Dashboard initialisieren
$dashboard = new AnalyticsDashboard($analyticsStorage);
// Statistiken abrufen
$stats = $dashboard->getStats(
new \DateTimeImmutable('-7 days'),
new \DateTimeImmutable()
);
// Spezifische Statistiken abrufen
$pageViewStats = $dashboard->getPageViewStats();
$errorStats = $dashboard->getErrorStats();
$performanceStats = $dashboard->getPerformanceStats();
$businessStats = $dashboard->getBusinessStats();
```
## Integration
### Service-Container
Der Analytics-Service wird automatisch registriert und kann per Dependency Injection verwendet werden:
```php
use App\Framework\Analytics\AnalyticsCollector;
class UserController
{
public function __construct(
private readonly AnalyticsCollector $analyticsCollector
) {}
public function register(Request $request): Response
{
// Benutzerregistrierung verarbeiten
// Geschäftsereignis erfassen
$this->analyticsCollector->trackBusinessEvent(
'user_registered',
['user_id' => $user->id, 'email_domain' => $emailDomain]
);
return $response;
}
}
```
### Event-Integration
Das Analytics Framework kann mit dem Event-System des Frameworks integriert werden:
```php
use App\Framework\Analytics\EventSubscribers\AnalyticsEventSubscriber;
// In der Bootstrap-Datei
$eventDispatcher->addSubscriber(new AnalyticsEventSubscriber($analyticsCollector));
```
## Leistungsoptimierung
### Sampling
Um die Leistung bei hohem Datenaufkommen zu verbessern, unterstützt das Analytics Framework Sampling:
```php
// Sampling-Rate konfigurieren (10% der Daten erfassen)
$config = new AnalyticsConfig(
$enabled = true,
$samplingRate = 0.1
);
```
### Pufferung
Das Analytics Framework verwendet Pufferung, um die Anzahl der Schreibvorgänge zu reduzieren:
```php
// Puffergröße konfigurieren
$config = new AnalyticsConfig(
$enabled = true,
$samplingRate = 1.0,
$securityAnalyticsEnabled = true,
$dataPath = '/var/www/html/storage/analytics',
$bufferSize = 1000 // Anzahl der Ereignisse im Puffer
);
```
### Asynchrone Verarbeitung
Für maximale Leistung kann die Analytics-Verarbeitung asynchron erfolgen:
```php
use App\Framework\Analytics\AsyncAnalyticsCollector;
// Asynchronen Collector verwenden
$asyncCollector = new AsyncAnalyticsCollector(
$analyticsCollector,
$queue
);
// Daten asynchron erfassen
$asyncCollector->trackPageView('/products');
```
## Datenverwaltung
### Datenaufbewahrung
Das Analytics Framework unterstützt automatische Datenaufbewahrungsrichtlinien:
```php
// Aufbewahrungsdauer konfigurieren
$config = new AnalyticsConfig(
$enabled = true,
$samplingRate = 1.0,
$securityAnalyticsEnabled = true,
$dataPath = '/var/www/html/storage/analytics',
$bufferSize = 1000,
$retentionDays = 365 // Daten für 1 Jahr aufbewahren
);
```
### Datenexport
Daten können für die weitere Analyse exportiert werden:
```php
use App\Framework\Analytics\Export\AnalyticsExporter;
// Exporter initialisieren
$exporter = new AnalyticsExporter($analyticsStorage);
// Daten exportieren
$csvData = $exporter->exportToCsv(
'page_view',
new \DateTimeImmutable('-7 days'),
new \DateTimeImmutable()
);
// Daten in Datei speichern
file_put_contents('page_views.csv', $csvData);
```
## Fehlerbehebung
### Logging
Das Analytics Framework bietet umfangreiches Logging für die Fehlerbehebung:
```php
// In der AnalyticsCollector-Klasse
$this->logger->info("Analytics: trackAction called with action={$action}, category={$category->value}");
$this->logger->error("Analytics: Failed to store data", ['error' => $e->getMessage()]);
```
### Häufige Probleme
#### Keine Daten werden erfasst
Mögliche Ursachen:
- Analytics ist deaktiviert (`enabled = false`)
- Sampling-Rate ist zu niedrig
- Fehler beim Speichern der Daten
Lösung:
- Überprüfen Sie die Konfiguration
- Erhöhen Sie die Sampling-Rate
- Überprüfen Sie die Logs auf Fehler
#### Hohe Serverbelastung
Mögliche Ursachen:
- Zu viele Ereignisse werden erfasst
- Puffergröße ist zu klein
- Synchrone Verarbeitung bei hohem Datenaufkommen
Lösung:
- Reduzieren Sie die Sampling-Rate
- Erhöhen Sie die Puffergröße
- Verwenden Sie asynchrone Verarbeitung
## Weiterführende Informationen
- [Analytics Konfiguration](configuration.md)
- [Analytics Beispiele](examples.md)
- [Performance-Monitoring](/components/performance/index.md)

View File

@@ -0,0 +1,430 @@
# Auth Module Configuration
**Konfiguration und Setup** für das Auth Module des Custom PHP Frameworks.
## Dependency Injection Setup
### Container Bindings
```php
use App\Framework\Auth\PasswordHasher;
use App\Framework\Auth\AuthenticationService;
use App\Framework\Cryptography\KeyDerivationFunction;
// services.php oder Container Initialization
$container->singleton(PasswordHasher::class, function(Container $container) {
return new PasswordHasher(
kdf: $container->get(KeyDerivationFunction::class),
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_STANDARD
);
});
$container->singleton(AuthenticationService::class, function(Container $container) {
return new AuthenticationService(
passwordHasher: $container->get(PasswordHasher::class),
sessionIdGenerator: $container->get(SessionIdGenerator::class),
repository: $container->get(AuthenticationRepository::class),
rateLimiter: $container->get(RateLimitService::class)
);
});
```
### Environment Configuration
```php
// .env Konfiguration
AUTH_SESSION_TIMEOUT=3600 # Session Timeout in Sekunden (1 Stunde)
AUTH_REMEMBER_TOKEN_EXPIRY=2592000 # Remember Token Expiry (30 Tage)
AUTH_MAX_LOGIN_ATTEMPTS=5 # Maximale Login-Versuche
AUTH_LOCKOUT_DURATION=900 # Account Lockout Duration (15 Minuten)
AUTH_RATE_LIMIT_WINDOW=300 # Rate Limit Window (5 Minuten)
# Password Hashing Configuration
AUTH_DEFAULT_ALGORITHM=argon2id # Standard Hash-Algorithmus
AUTH_DEFAULT_SECURITY_LEVEL=standard # low|standard|high
AUTH_PASSWORD_MIN_LENGTH=8 # Minimale Passwort-Länge
AUTH_PASSWORD_MAX_LENGTH=4096 # Maximale Passwort-Länge
# Session Security
AUTH_SESSION_REGENERATE_ON_LOGIN=true # Session ID bei Login regenerieren
AUTH_CHECK_IP_CONSISTENCY=false # IP-Konsistenz-Prüfung (optional)
AUTH_REMEMBER_TOKEN_LENGTH=32 # Remember Token Länge (Bytes)
```
### Typed Configuration Class
```php
final readonly class AuthConfig
{
public function __construct(
// Session Configuration
public int $sessionTimeout = 3600,
public int $rememberTokenExpiry = 2592000,
public bool $sessionRegenerateOnLogin = true,
public bool $checkIpConsistency = false,
public int $rememberTokenLength = 32,
// Security Configuration
public int $maxLoginAttempts = 5,
public int $lockoutDuration = 900,
public int $rateLimitWindow = 300,
// Password Configuration
public string $defaultAlgorithm = 'argon2id',
public string $defaultSecurityLevel = 'standard',
public int $passwordMinLength = 8,
public int $passwordMaxLength = 4096,
// Validation Configuration
public bool $enforcePasswordComplexity = true,
public bool $checkCommonPasswords = true,
public bool $preventSequentialChars = true,
public int $minPasswordScore = 50
) {}
public static function fromEnvironment(Environment $env): self
{
return new self(
sessionTimeout: $env->getInt(EnvKey::AUTH_SESSION_TIMEOUT, 3600),
rememberTokenExpiry: $env->getInt(EnvKey::AUTH_REMEMBER_TOKEN_EXPIRY, 2592000),
sessionRegenerateOnLogin: $env->getBool(EnvKey::AUTH_SESSION_REGENERATE_ON_LOGIN, true),
checkIpConsistency: $env->getBool(EnvKey::AUTH_CHECK_IP_CONSISTENCY, false),
rememberTokenLength: $env->getInt(EnvKey::AUTH_REMEMBER_TOKEN_LENGTH, 32),
maxLoginAttempts: $env->getInt(EnvKey::AUTH_MAX_LOGIN_ATTEMPTS, 5),
lockoutDuration: $env->getInt(EnvKey::AUTH_LOCKOUT_DURATION, 900),
rateLimitWindow: $env->getInt(EnvKey::AUTH_RATE_LIMIT_WINDOW, 300),
defaultAlgorithm: $env->get(EnvKey::AUTH_DEFAULT_ALGORITHM, 'argon2id'),
defaultSecurityLevel: $env->get(EnvKey::AUTH_DEFAULT_SECURITY_LEVEL, 'standard'),
passwordMinLength: $env->getInt(EnvKey::AUTH_PASSWORD_MIN_LENGTH, 8),
passwordMaxLength: $env->getInt(EnvKey::AUTH_PASSWORD_MAX_LENGTH, 4096),
enforcePasswordComplexity: $env->getBool(EnvKey::AUTH_ENFORCE_PASSWORD_COMPLEXITY, true),
checkCommonPasswords: $env->getBool(EnvKey::AUTH_CHECK_COMMON_PASSWORDS, true),
preventSequentialChars: $env->getBool(EnvKey::AUTH_PREVENT_SEQUENTIAL_CHARS, true),
minPasswordScore: $env->getInt(EnvKey::AUTH_MIN_PASSWORD_SCORE, 50)
);
}
public function getSecurityLevelParameters(string $algorithm): array
{
return match ([$algorithm, $this->defaultSecurityLevel]) {
['argon2id', 'low'] => [
'memory_cost' => 32768, // 32 MB
'time_cost' => 2,
'threads' => 2
],
['argon2id', 'standard'] => [
'memory_cost' => 65536, // 64 MB
'time_cost' => 4,
'threads' => 3
],
['argon2id', 'high'] => [
'memory_cost' => 131072, // 128 MB
'time_cost' => 6,
'threads' => 4
],
['pbkdf2-sha256', 'low'] => ['iterations' => 50000],
['pbkdf2-sha256', 'standard'] => ['iterations' => 100000],
['pbkdf2-sha256', 'high'] => ['iterations' => 200000],
default => []
};
}
}
```
## Database Schema
### Required Tables
```sql
-- Benutzer-Tabelle (beispielhaft)
CREATE TABLE users (
id VARCHAR(255) PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(100) UNIQUE,
password_hash TEXT NOT NULL,
password_algorithm VARCHAR(50) NOT NULL DEFAULT 'argon2id',
password_parameters JSON,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
last_login_at DATETIME NULL,
is_active BOOLEAN DEFAULT TRUE,
INDEX idx_email (email),
INDEX idx_username (username),
INDEX idx_active (is_active)
);
-- Session-Tabelle
CREATE TABLE auth_sessions (
id VARCHAR(255) PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
created_at DATETIME NOT NULL,
expires_at DATETIME NOT NULL,
last_activity DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_id (user_id),
INDEX idx_expires_at (expires_at),
INDEX idx_last_activity (last_activity)
);
-- Remember Token Tabelle
CREATE TABLE auth_remember_tokens (
token_hash VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL,
expires_at DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_id (user_id),
INDEX idx_expires_at (expires_at)
);
-- Failed Login Attempts Tabelle
CREATE TABLE auth_failed_attempts (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(255),
identifier VARCHAR(255),
ip_address VARCHAR(45),
attempted_at DATETIME NOT NULL,
reason VARCHAR(100),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user_id (user_id),
INDEX idx_identifier (identifier),
INDEX idx_ip_address (ip_address),
INDEX idx_attempted_at (attempted_at)
);
-- Security Events Tabelle (optional für Logging)
CREATE TABLE auth_security_events (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
event_type VARCHAR(50) NOT NULL,
user_id VARCHAR(255),
session_id VARCHAR(255),
ip_address VARCHAR(45),
user_agent TEXT,
event_data JSON,
created_at DATETIME NOT NULL,
INDEX idx_event_type (event_type),
INDEX idx_user_id (user_id),
INDEX idx_created_at (created_at),
INDEX idx_ip_address (ip_address)
);
```
### Migration Commands
```bash
# Migration erstellen
php console.php make:migration CreateAuthTables Auth
# Migration ausführen
php console.php db:migrate
# Migration Status prüfen
php console.php db:status
```
## Security Level Configuration
### Password Hashing Levels
```php
// Niedrige Sicherheit (Development, Testing)
$lowSecurity = new PasswordHasher(
kdf: $kdf,
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_LOW
);
// Standard Sicherheit (Production Default)
$standardSecurity = new PasswordHasher(
kdf: $kdf,
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_STANDARD
);
// Hohe Sicherheit (Banking, Healthcare)
$highSecurity = new PasswordHasher(
kdf: $kdf,
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_HIGH
);
```
### Algorithm Performance Comparison
| Algorithm | Level | Memory | Time | Iterations | Performance | Security |
|-----------|-------|---------|------|------------|-------------|----------|
| Argon2ID | Low | 32 MB | 2 | - | Fast | Good |
| Argon2ID | Standard | 64 MB | 4 | - | Medium | Excellent |
| Argon2ID | High | 128 MB | 6 | - | Slow | Maximum |
| PBKDF2-SHA256 | Low | - | - | 50,000 | Fast | Good |
| PBKDF2-SHA256 | Standard | - | - | 100,000 | Fast | Good |
| PBKDF2-SHA256 | High | - | - | 200,000 | Medium | Good |
| Scrypt | Standard | 16 MB | - | - | Medium | Good |
## Rate Limiting Configuration
### Redis-based Rate Limiting
```php
use App\Framework\Auth\RateLimit\RedisRateLimitService;
$rateLimiter = new RedisRateLimitService(
redis: $redis,
config: new RateLimitConfig(
maxAttempts: 5,
windowSeconds: 300,
lockoutDuration: 900
)
);
```
### File-based Rate Limiting
```php
use App\Framework\Auth\RateLimit\FileRateLimitService;
$rateLimiter = new FileRateLimitService(
cacheDir: '/tmp/auth_rate_limits',
config: new RateLimitConfig(
maxAttempts: 5,
windowSeconds: 300,
lockoutDuration: 900,
cleanupInterval: 3600 // Cleanup alte Einträge jede Stunde
)
);
```
## Monitoring & Logging
### Security Event Configuration
```php
// Security Event Handler
final readonly class SecurityEventHandler
{
public function __construct(
private Logger $logger,
private ?AlertingService $alerting = null
) {}
public function handle(SecurityEvent $event): void
{
// Log alle Security Events
$this->logger->warning('Security Event', [
'event_type' => $event->getType(),
'user_id' => $event->getUserId(),
'ip_address' => (string) $event->getIpAddress(),
'data' => $event->getData()
]);
// Kritische Events alarmieren
if ($event->isCritical()) {
$this->alerting?->sendAlert($event);
}
}
}
```
### Performance Monitoring
```php
// Performance Metrics für Password Hashing
$start = microtime(true);
$hashedPassword = $passwordHasher->hash($password);
$hashTime = microtime(true) - $start;
$this->metrics->histogram('auth.password_hash_duration', $hashTime, [
'algorithm' => $hashedPassword->getAlgorithm(),
'security_level' => $securityLevel
]);
```
## Production Deployment
### Environment-specific Configuration
```php
// Production
AUTH_DEFAULT_SECURITY_LEVEL=high
AUTH_SESSION_TIMEOUT=1800 # 30 Minuten
AUTH_CHECK_IP_CONSISTENCY=true # Striktere IP-Prüfung
AUTH_MAX_LOGIN_ATTEMPTS=3 # Weniger Versuche
AUTH_LOCKOUT_DURATION=3600 # 1 Stunde Lockout
// Development
AUTH_DEFAULT_SECURITY_LEVEL=low
AUTH_SESSION_TIMEOUT=86400 # 24 Stunden
AUTH_CHECK_IP_CONSISTENCY=false
AUTH_MAX_LOGIN_ATTEMPTS=10
AUTH_LOCKOUT_DURATION=300 # 5 Minuten
// Testing
AUTH_DEFAULT_SECURITY_LEVEL=low
AUTH_SESSION_TIMEOUT=3600
AUTH_MAX_LOGIN_ATTEMPTS=5
AUTH_LOCKOUT_DURATION=60 # 1 Minute
```
### Security Headers Configuration
```php
// Middleware für Auth-bezogene Security Headers
final readonly class AuthSecurityHeadersMiddleware
{
public function handle(HttpRequest $request, callable $next): HttpResponse
{
$response = $next($request);
if ($request->getUri()->getPath() === '/login') {
$response = $response->withHeader('X-Frame-Options', 'DENY');
$response = $response->withHeader('X-Content-Type-Options', 'nosniff');
$response = $response->withHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
}
return $response;
}
}
```
## Backup & Recovery
### Session Cleanup
```bash
# Cron Job für Session Cleanup (täglich)
0 2 * * * php /path/to/console.php auth:cleanup-expired-sessions
# Manual Cleanup
php console.php auth:cleanup-expired-sessions
php console.php auth:cleanup-expired-tokens
```
### Data Retention
```php
// Cleanup Commands
final readonly class CleanupExpiredSessionsCommand
{
public function execute(): void
{
$expiredCount = $this->repository->deleteExpiredSessions();
$this->output->writeln("Deleted {$expiredCount} expired sessions");
$tokenCount = $this->repository->deleteExpiredRememberTokens();
$this->output->writeln("Deleted {$tokenCount} expired remember tokens");
$attemptCount = $this->repository->cleanupOldFailedAttempts(days: 30);
$this->output->writeln("Cleaned up {$attemptCount} old failed attempts");
}
}

View File

@@ -0,0 +1,797 @@
# Auth Module Examples
**Praktische Implementierungsbeispiele** für das Auth Module des Custom PHP Frameworks.
## Basic Authentication Flow
### User Registration
```php
use App\Framework\Auth\PasswordHasher;
use App\Framework\Auth\PasswordValidationResult;
final readonly class UserRegistrationService
{
public function __construct(
private PasswordHasher $passwordHasher,
private UserRepository $userRepository
) {}
public function register(
string $email,
#[SensitiveParameter] string $password,
string $username = null
): RegistrationResult {
// Validate password strength
$validation = $this->passwordHasher->validatePasswordStrength($password);
if (!$validation->isValid) {
return RegistrationResult::passwordValidationFailed($validation);
}
// Check if user exists
if ($this->userRepository->findByEmail($email)) {
return RegistrationResult::failed('User with this email already exists');
}
// Hash password
$hashedPassword = $this->passwordHasher->hash($password);
// Create user
$user = new User(
id: Uuid::generate(),
email: new Email($email),
username: $username,
hashedPassword: $hashedPassword,
createdAt: new \DateTimeImmutable()
);
$this->userRepository->save($user);
return RegistrationResult::success($user);
}
}
```
### Login Implementation
```php
use App\Framework\Auth\AuthenticationService;
use App\Framework\Http\IpAddress;
use App\Framework\Http\Session\SessionManager;
#[Route(path: '/login', method: Method::POST)]
final readonly class LoginController
{
public function __construct(
private AuthenticationService $authService,
private SessionManager $sessionManager
) {}
public function login(LoginRequest $request): JsonResult
{
$ipAddress = IpAddress::fromRequest();
$result = $this->authService->authenticate(
identifier: $request->email,
password: $request->password,
ipAddress: $ipAddress,
remember: $request->remember ?? false
);
if ($result->isSuccess()) {
$user = $result->getUser();
$session = $result->getSession();
// Store session in session manager
$this->sessionManager->start($session->getId());
$this->sessionManager->set('user_id', $user->getId());
$this->sessionManager->set('authenticated_at', time());
$responseData = [
'success' => true,
'user' => [
'id' => $user->getId(),
'email' => (string) $user->getEmail(),
'username' => $user->getUsername()
],
'session' => [
'id' => $session->getId()->toString(),
'expires_at' => $session->getExpiresAt()->format(\DateTimeInterface::ATOM)
]
];
// Add remember token to response if requested
if ($rememberToken = $result->getRememberToken()) {
$responseData['remember_token'] = $rememberToken->getPlainTextValue();
// Set secure HTTP-only cookie
setcookie(
'remember_token',
$rememberToken->getPlainTextValue(),
[
'expires' => $rememberToken->getExpiresAt()->getTimestamp(),
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]
);
}
return new JsonResult($responseData);
}
// Handle different failure types
return match (true) {
$result->isRateLimited() => new JsonResult([
'success' => false,
'error' => 'Too many attempts',
'retry_after' => $result->getRetryAfter()
], 429),
$result->isAccountLocked() => new JsonResult([
'success' => false,
'error' => 'Account temporarily locked',
'locked_until' => $result->getLockoutExpiresAt()?->format(\DateTimeInterface::ATOM)
], 423),
default => new JsonResult([
'success' => false,
'error' => 'Invalid credentials'
], 401)
};
}
}
```
### Auto-Login with Remember Token
```php
#[Route(path: '/auth/check', method: Method::GET)]
final readonly class AuthCheckController
{
public function __construct(
private AuthenticationService $authService,
private SessionManager $sessionManager
) {}
public function check(HttpRequest $request): JsonResult
{
$sessionId = $this->sessionManager->getCurrentSessionId();
// Try session authentication first
if ($sessionId) {
$result = $this->authService->authenticateWithSession(
$sessionId,
IpAddress::fromRequest()
);
if ($result->isSuccess()) {
return new JsonResult([
'authenticated' => true,
'user' => $this->formatUser($result->getUser()),
'session' => $this->formatSession($result->getSession())
]);
}
}
// Try remember token authentication
$rememberToken = $request->getCookie('remember_token');
if ($rememberToken) {
$result = $this->authService->authenticateWithRememberToken(
$rememberToken,
IpAddress::fromRequest()
);
if ($result->isSuccess()) {
// Start new session
$session = $result->getSession();
$this->sessionManager->start($session->getId());
$this->sessionManager->set('user_id', $result->getUser()->getId());
// Update remember token cookie
$newRememberToken = $result->getRememberToken();
if ($newRememberToken) {
setcookie(
'remember_token',
$newRememberToken->getPlainTextValue(),
[
'expires' => $newRememberToken->getExpiresAt()->getTimestamp(),
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]
);
}
return new JsonResult([
'authenticated' => true,
'user' => $this->formatUser($result->getUser()),
'session' => $this->formatSession($session)
]);
}
}
return new JsonResult(['authenticated' => false]);
}
private function formatUser(User $user): array
{
return [
'id' => $user->getId(),
'email' => (string) $user->getEmail(),
'username' => $user->getUsername(),
'last_login' => $user->getLastLoginAt()?->format(\DateTimeInterface::ATOM)
];
}
private function formatSession(AuthenticationSession $session): array
{
return [
'id' => $session->getId()->toString(),
'created_at' => $session->getCreatedAt()->format(\DateTimeInterface::ATOM),
'expires_at' => $session->getExpiresAt()->format(\DateTimeInterface::ATOM),
'last_activity' => $session->getLastActivity()->format(\DateTimeInterface::ATOM)
];
}
}
```
## Password Management
### Password Change
```php
#[Route(path: '/account/change-password', method: Method::POST)]
final readonly class ChangePasswordController
{
public function __construct(
private AuthenticationService $authService,
private SessionManager $sessionManager
) {}
public function changePassword(ChangePasswordRequest $request): JsonResult
{
$userId = $this->sessionManager->get('user_id');
if (!$userId) {
return new JsonResult(['error' => 'Not authenticated'], 401);
}
$result = $this->authService->changePassword(
userId: $userId,
currentPassword: $request->currentPassword,
newPassword: $request->newPassword
);
if ($result->isSuccess()) {
return new JsonResult([
'success' => true,
'message' => 'Password changed successfully'
]);
}
if ($result->hasValidationErrors()) {
return new JsonResult([
'success' => false,
'validation_errors' => $result->getValidation()->toArray()
], 400);
}
return new JsonResult([
'success' => false,
'error' => $result->getErrorMessage()
], 400);
}
}
```
### Password Reset Flow
```php
final readonly class PasswordResetService
{
public function __construct(
private PasswordHasher $passwordHasher,
private UserRepository $userRepository,
private TokenGenerator $tokenGenerator,
private EmailService $emailService
) {}
public function initiateReset(string $email): PasswordResetResult
{
$user = $this->userRepository->findByEmail($email);
if (!$user) {
// Return success even if user not found (security)
return PasswordResetResult::success();
}
// Generate secure reset token
$token = $this->tokenGenerator->generateSecureToken(32);
$tokenHash = Hash::sha256($token);
$expiresAt = new \DateTimeImmutable('+1 hour');
$resetToken = new PasswordResetToken(
hash: $tokenHash,
userId: $user->getId(),
createdAt: new \DateTimeImmutable(),
expiresAt: $expiresAt
);
$this->userRepository->storePasswordResetToken($resetToken);
// Send reset email
$this->emailService->sendPasswordResetEmail($user->getEmail(), $token);
return PasswordResetResult::success();
}
public function resetPassword(
string $token,
#[SensitiveParameter] string $newPassword
): PasswordResetResult {
$tokenHash = Hash::sha256($token);
$resetToken = $this->userRepository->findPasswordResetToken($tokenHash);
if (!$resetToken || $resetToken->isExpired()) {
return PasswordResetResult::failed('Invalid or expired reset token');
}
$user = $this->userRepository->findById($resetToken->getUserId());
if (!$user) {
return PasswordResetResult::failed('User not found');
}
// Validate new password
$validation = $this->passwordHasher->validatePasswordStrength($newPassword);
if (!$validation->isValid) {
return PasswordResetResult::validationFailed($validation);
}
// Hash new password
$hashedPassword = $this->passwordHasher->hash($newPassword);
// Update password and cleanup
$this->userRepository->updateUserPassword($user->getId(), $hashedPassword);
$this->userRepository->deletePasswordResetToken($tokenHash);
$this->userRepository->deleteAllUserSessions($user->getId());
return PasswordResetResult::success();
}
}
```
## Advanced Security Features
### Multi-Factor Authentication Setup
```php
final readonly class MfaService
{
public function __construct(
private PasswordHasher $passwordHasher,
private TotpService $totpService,
private UserRepository $userRepository
) {}
public function setupTotp(string $userId): MfaSetupResult
{
$user = $this->userRepository->findById($userId);
if (!$user) {
return MfaSetupResult::failed('User not found');
}
// Generate TOTP secret
$secret = $this->totpService->generateSecret();
// Create QR code data
$qrCodeUri = $this->totpService->getQrCodeUri(
secret: $secret,
accountName: (string) $user->getEmail(),
issuer: 'Your App Name'
);
// Store temporary secret (not activated until verified)
$tempSecret = new TempTotpSecret(
userId: $userId,
secret: $secret,
createdAt: new \DateTimeImmutable(),
expiresAt: new \DateTimeImmutable('+10 minutes')
);
$this->userRepository->storeTempTotpSecret($tempSecret);
return MfaSetupResult::success($secret, $qrCodeUri);
}
public function verifyAndActivateTotp(
string $userId,
string $totpCode
): MfaActivationResult {
$tempSecret = $this->userRepository->findTempTotpSecret($userId);
if (!$tempSecret || $tempSecret->isExpired()) {
return MfaActivationResult::failed('Setup expired, please restart');
}
// Verify TOTP code
if (!$this->totpService->verify($totpCode, $tempSecret->getSecret())) {
return MfaActivationResult::failed('Invalid TOTP code');
}
// Activate TOTP for user
$this->userRepository->activateTotpForUser($userId, $tempSecret->getSecret());
$this->userRepository->deleteTempTotpSecret($userId);
// Generate backup codes
$backupCodes = $this->generateBackupCodes($userId);
return MfaActivationResult::success($backupCodes);
}
private function generateBackupCodes(string $userId): array
{
$codes = [];
for ($i = 0; $i < 8; $i++) {
$code = $this->generateReadableCode();
$codes[] = $code;
$backupCode = new MfaBackupCode(
userId: $userId,
code: Hash::sha256($code),
createdAt: new \DateTimeImmutable(),
usedAt: null
);
$this->userRepository->storeBackupCode($backupCode);
}
return $codes;
}
private function generateReadableCode(): string
{
// Generate 8-digit backup code
return sprintf('%04d-%04d', random_int(1000, 9999), random_int(1000, 9999));
}
}
```
### Session Management with Device Tracking
```php
final readonly class DeviceTrackingService
{
public function __construct(
private AuthenticationService $authService,
private SessionRepository $sessionRepository
) {}
public function authenticateWithDeviceTracking(
string $identifier,
#[SensitiveParameter] string $password,
HttpRequest $request
): AuthenticationResult {
$ipAddress = IpAddress::fromRequest();
$userAgent = $request->server->getUserAgent();
$result = $this->authService->authenticate($identifier, $password, $ipAddress);
if ($result->isSuccess()) {
$session = $result->getSession();
$user = $result->getUser();
// Create device fingerprint
$deviceInfo = new DeviceInfo(
userAgent: $userAgent,
ipAddress: $ipAddress,
screenResolution: $request->headers->get('X-Screen-Resolution'),
timezone: $request->headers->get('X-Timezone'),
language: $request->headers->get('Accept-Language')
);
// Check if this is a new device
$isNewDevice = !$this->sessionRepository->hasRecentSessionForDevice(
userId: $user->getId(),
deviceFingerprint: $deviceInfo->getFingerprint(),
withinDays: 30
);
if ($isNewDevice) {
// Send security notification
$this->sendNewDeviceNotification($user, $deviceInfo, $ipAddress);
}
// Update session with device info
$this->sessionRepository->updateSessionDeviceInfo($session->getId(), $deviceInfo);
}
return $result;
}
public function listActiveSessions(string $userId): array
{
$sessions = $this->sessionRepository->getActiveSessionsForUser($userId);
return array_map(function (AuthenticationSession $session) {
$deviceInfo = $this->sessionRepository->getSessionDeviceInfo($session->getId());
return [
'id' => $session->getId()->toString(),
'created_at' => $session->getCreatedAt()->format(\DateTimeInterface::ATOM),
'last_activity' => $session->getLastActivity()->format(\DateTimeInterface::ATOM),
'ip_address' => (string) $session->getIpAddress(),
'device_info' => $deviceInfo?->toArray(),
'is_current' => $this->isCurrentSession($session->getId())
];
}, $sessions);
}
public function revokeSession(string $userId, SessionId $sessionId): bool
{
// Verify session belongs to user
$session = $this->sessionRepository->findSessionById($sessionId);
if (!$session || $session->getUserId() !== $userId) {
return false;
}
return $this->authService->logout($sessionId);
}
private function sendNewDeviceNotification(
User $user,
DeviceInfo $deviceInfo,
IpAddress $ipAddress
): void {
// Implementation would send email/SMS notification
// about login from new device
}
private function isCurrentSession(SessionId $sessionId): bool
{
// Check if this is the current session
return session_id() === $sessionId->toString();
}
}
```
## Rate Limiting & Security
### Custom Rate Limiting Implementation
```php
final readonly class CustomRateLimitService implements RateLimitService
{
public function __construct(
private Cache $cache,
private int $maxAttempts = 5,
private int $windowSeconds = 300,
private int $lockoutDuration = 900
) {}
public function isRateLimited(IpAddress $ipAddress, string $action): bool
{
$key = $this->getRateLimitKey($ipAddress, $action);
$attempts = $this->cache->get($key, 0);
return $attempts >= $this->maxAttempts;
}
public function recordAttempt(IpAddress $ipAddress, string $action): void
{
$key = $this->getRateLimitKey($ipAddress, $action);
$attempts = $this->cache->get($key, 0);
$this->cache->set($key, $attempts + 1, $this->windowSeconds);
if ($attempts + 1 >= $this->maxAttempts) {
$lockoutKey = $this->getLockoutKey($ipAddress, $action);
$this->cache->set($lockoutKey, true, $this->lockoutDuration);
}
}
public function clearAttempts(IpAddress $ipAddress, string $action): void
{
$key = $this->getRateLimitKey($ipAddress, $action);
$lockoutKey = $this->getLockoutKey($ipAddress, $action);
$this->cache->forget($key);
$this->cache->forget($lockoutKey);
}
public function getRetryAfter(IpAddress $ipAddress, string $action): int
{
$lockoutKey = $this->getLockoutKey($ipAddress, $action);
$lockoutExpiry = $this->cache->get($lockoutKey);
if (!$lockoutExpiry) {
return 0;
}
return max(0, $lockoutExpiry - time());
}
private function getRateLimitKey(IpAddress $ipAddress, string $action): string
{
return sprintf('rate_limit:%s:%s', $action, (string) $ipAddress);
}
private function getLockoutKey(IpAddress $ipAddress, string $action): string
{
return sprintf('lockout:%s:%s', $action, (string) $ipAddress);
}
}
```
## Testing Examples
### Authentication Service Testing
```php
use PHPUnit\Framework\TestCase;
use App\Framework\Auth\AuthenticationService;
final class AuthenticationServiceTest extends TestCase
{
private AuthenticationService $authService;
private PasswordHasher $passwordHasher;
private MockAuthenticationRepository $repository;
protected function setUp(): void
{
$this->passwordHasher = new PasswordHasher(
kdf: new MockKeyDerivationFunction(),
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_LOW // Fast for testing
);
$this->repository = new MockAuthenticationRepository();
$this->authService = new AuthenticationService(
passwordHasher: $this->passwordHasher,
sessionIdGenerator: new MockSessionIdGenerator(),
repository: $this->repository
);
}
public function test_successful_authentication(): void
{
$password = 'SecurePassword123!';
$hashedPassword = $this->passwordHasher->hash($password);
$user = new User(
id: 'user-1',
email: new Email('user@example.com'),
username: 'testuser',
hashedPassword: $hashedPassword
);
$this->repository->addUser($user);
$result = $this->authService->authenticate(
identifier: 'user@example.com',
password: $password,
ipAddress: IpAddress::localhost()
);
$this->assertTrue($result->isSuccess());
$this->assertEquals($user->getId(), $result->getUser()->getId());
$this->assertInstanceOf(AuthenticationSession::class, $result->getSession());
}
public function test_failed_authentication_with_invalid_password(): void
{
$hashedPassword = $this->passwordHasher->hash('CorrectPassword123!');
$user = new User(
id: 'user-1',
email: new Email('user@example.com'),
username: 'testuser',
hashedPassword: $hashedPassword
);
$this->repository->addUser($user);
$result = $this->authService->authenticate(
identifier: 'user@example.com',
password: 'WrongPassword',
ipAddress: IpAddress::localhost()
);
$this->assertFalse($result->isSuccess());
$this->assertEquals('Invalid credentials', $result->getErrorMessage());
}
public function test_account_lockout_after_max_attempts(): void
{
$hashedPassword = $this->passwordHasher->hash('CorrectPassword123!');
$user = new User(
id: 'user-1',
email: new Email('user@example.com'),
username: 'testuser',
hashedPassword: $hashedPassword
);
$this->repository->addUser($user);
// Simulate failed attempts
for ($i = 0; $i < AuthenticationService::MAX_LOGIN_ATTEMPTS; $i++) {
$this->authService->authenticate(
identifier: 'user@example.com',
password: 'WrongPassword',
ipAddress: IpAddress::localhost()
);
}
// Next attempt should be locked
$result = $this->authService->authenticate(
identifier: 'user@example.com',
password: 'CorrectPassword123!',
ipAddress: IpAddress::localhost()
);
$this->assertTrue($result->isAccountLocked());
$this->assertInstanceOf(\DateTimeImmutable::class, $result->getLockoutExpiresAt());
}
}
```
### Password Hashing Testing
```php
final class PasswordHasherTest extends TestCase
{
private PasswordHasher $passwordHasher;
protected function setUp(): void
{
$this->passwordHasher = new PasswordHasher(
kdf: new MockKeyDerivationFunction(),
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_LOW
);
}
public function test_password_hashing_and_verification(): void
{
$password = 'TestPassword123!';
$hashedPassword = $this->passwordHasher->hash($password);
$this->assertInstanceOf(HashedPassword::class, $hashedPassword);
$this->assertEquals('argon2id', $hashedPassword->getAlgorithm());
$this->assertTrue($this->passwordHasher->verify($password, $hashedPassword));
$this->assertFalse($this->passwordHasher->verify('WrongPassword', $hashedPassword));
}
public function test_password_strength_validation(): void
{
$weakPassword = 'weak';
$strongPassword = 'StrongPassword123!@#';
$weakValidation = $this->passwordHasher->validatePasswordStrength($weakPassword);
$strongValidation = $this->passwordHasher->validatePasswordStrength($strongPassword);
$this->assertFalse($weakValidation->isValid);
$this->assertNotEmpty($weakValidation->errors);
$this->assertEquals(PasswordStrength::WEAK, $weakValidation->strength);
$this->assertTrue($strongValidation->isValid);
$this->assertEmpty($strongValidation->errors);
$this->assertContains($strongValidation->strength, [
PasswordStrength::STRONG,
PasswordStrength::VERY_STRONG
]);
}
public function test_secure_password_generation(): void
{
$password = $this->passwordHasher->generateSecurePassword(16);
$this->assertEquals(16, strlen($password));
$validation = $this->passwordHasher->validatePasswordStrength($password);
$this->assertTrue($validation->isValid);
$this->assertGreaterThanOrEqual(70, $validation->strengthScore);
}
}

View File

@@ -0,0 +1,310 @@
# Auth Module
**Sichere Authentifizierungs- und Autorisierungskomponenten** für das Custom PHP Framework.
## Überblick
Das Auth Module bietet umfassende Sicherheitsfeatures für Benutzerauthentifizierung, Passwort-Management und Session-Verwaltung. Es integriert sich nahtlos mit dem Cryptography Module für maximale Sicherheit.
## Kernkomponenten
### PasswordHasher Service
Sichere Passwort-Hashing und -Verifizierung mit automatischem Rehashing.
```php
use App\Framework\Auth\PasswordHasher;
use App\Framework\Cryptography\KeyDerivationFunction;
$passwordHasher = new PasswordHasher(
kdf: $keyDerivationFunction,
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_STANDARD
);
// Passwort hashen
$hashedPassword = $passwordHasher->hash('userPassword123');
// Passwort verifizieren
$isValid = $passwordHasher->verify('userPassword123', $hashedPassword);
// Passwort-Stärke validieren
$validation = $passwordHasher->validatePasswordStrength('userPassword123');
if (!$validation->isValid) {
echo implode(', ', $validation->errors);
}
```
### HashedPassword Value Object
Immutables Value Object für gehashte Passwörter mit Metadaten.
```php
use App\Framework\Auth\HashedPassword;
use App\Framework\Auth\PasswordStrength;
// Aus DerivedKey erstellen
$hashedPassword = HashedPassword::fromDerivedKey($derivedKey);
// Eigenschaften abrufen
$algorithm = $hashedPassword->getAlgorithm(); // 'argon2id'
$parameters = $hashedPassword->getParameters(); // ['memory_cost' => 65536, ...]
$strength = $hashedPassword->getStrength(); // PasswordStrength::STRONG
$createdAt = $hashedPassword->getCreatedAt(); // DateTimeImmutable
// Rehashing-Prüfung
$needsRehash = $hashedPassword->needsRehash('argon2id', [
'memory_cost' => 131072, // Höhere Sicherheitsanforderungen
'time_cost' => 6
]);
// Sicherheits-Bewertung
$assessment = $hashedPassword->assessSecurity();
echo $assessment->getSummary(); // "Strong security with Argon2ID (2024 standards)"
```
### AuthenticationService
Zentrale Authentifizierungslogik mit erweiterten Sicherheitsfeatures.
```php
use App\Framework\Auth\AuthenticationService;
use App\Framework\Http\IpAddress;
$authService = new AuthenticationService(
passwordHasher: $passwordHasher,
sessionIdGenerator: $sessionIdGenerator,
repository: $authRepository
);
// Benutzer authentifizieren
$result = $authService->authenticate(
identifier: 'user@example.com',
password: 'userPassword123',
ipAddress: IpAddress::from('192.168.1.1'),
remember: true
);
if ($result->isSuccess()) {
$user = $result->getUser();
$session = $result->getSession();
$rememberToken = $result->getRememberToken(); // nullable
}
// Mit Session authentifizieren
$result = $authService->authenticateWithSession(
$sessionId,
IpAddress::from('192.168.1.1')
);
// Mit Remember Token authentifizieren
$result = $authService->authenticateWithRememberToken(
$tokenValue,
IpAddress::from('192.168.1.1')
);
```
### PasswordValidationResult Value Object
Detaillierte Passwort-Validierungsergebnisse.
```php
use App\Framework\Auth\PasswordValidationResult;
$validation = $passwordHasher->validatePasswordStrength('weakpass');
// Validierungsstatus prüfen
$isValid = $validation->isValid; // false
$errors = $validation->errors; // ['Password must be at least 8 characters long']
$warnings = $validation->warnings; // ['Consider using more character types']
$score = $validation->strengthScore; // 45
$strength = $validation->strength; // PasswordStrength::WEAK
// Sicherheitslevel prüfen
$meetsMinimum = $validation->meetsMinimumRequirements(); // false
$isRecommended = $validation->isRecommended(); // false
// Zusammenfassung
echo $validation->getSummary();
// "Password does not meet requirements: Password must be at least 8 characters long"
// API-Response Format
$apiResponse = $validation->toArray();
```
## Sicherheitsfeatures
### Rate Limiting & Account Lockout
Schutz vor Brute-Force-Angriffen durch intelligente Rate-Limitierung.
```php
// Automatisches Rate Limiting in AuthenticationService
const MAX_LOGIN_ATTEMPTS = 5;
const LOCKOUT_DURATION = 900; // 15 Minuten
const RATE_LIMIT_WINDOW = 300; // 5 Minuten
// Rate Limiting wird automatisch angewendet
$result = $authService->authenticate($email, $password, $ipAddress);
if ($result->isRateLimited()) {
$retryAfter = $result->getRetryAfter();
echo "Rate limit exceeded. Retry after {$retryAfter} seconds.";
}
if ($result->isAccountLocked()) {
$expiresAt = $result->getLockoutExpiresAt();
echo "Account locked until {$expiresAt->format('Y-m-d H:i:s')}";
}
```
### Session-Sicherheit
Sichere Session-Verwaltung mit IP-Tracking und automatischer Rotation.
```php
// Sessions werden automatisch erstellt und verwaltet
const SESSION_TIMEOUT = 3600; // 1 Stunde
const REMEMBER_TOKEN_LENGTH = 32; // 256-bit Token
// Session-Features:
// - Automatische IP-Konsistenz-Prüfung
// - Session-Timeout-Management
// - Aktivitäts-Tracking
// - Sichere Session-IDs über SessionIdGenerator
```
### Token-Hashing mit Hash Value Object
Sichere Token-Behandlung mit typisierter Hash-Verwaltung.
```php
// Remember Tokens werden sicher gehasht
private function hashToken(string $token): Hash
{
return Hash::sha256($token);
}
// Vorteile:
// - Typsicherheit für Hash-Werte
// - Explizite Algorithmus-Angabe (SHA-256)
// - Timing-safe Vergleiche mit hash_equals()
// - Framework-konsistente Hash-Behandlung
// - Automatische Hash-Validierung
```
### Passwort-Sicherheit
Umfassende Passwort-Validierung und -Bewertung.
```php
// Automatische Passwort-Stärke-Bewertung
$validation = $passwordHasher->validatePasswordStrength($password);
// Validierungsregeln:
// - Minimale Länge (8+ Zeichen)
// - Komplexitätsanforderungen (2+ Zeichentypen)
// - Häufige Muster-Erkennung
// - Sequenzielle Zeichen-Prüfung
// - Exzessive Wiederholungen
// Sichere Passwort-Generierung
$securePassword = $passwordHasher->generateSecurePassword(
length: 16,
includeUppercase: true,
includeLowercase: true,
includeNumbers: true,
includeSpecialChars: true,
excludeChars: '0O1l' // Mehrdeutige Zeichen ausschließen
);
```
## Integration mit Framework
### Abhängigkeiten
Das Auth Module nutzt vorhandene Framework-Komponenten:
- **Cryptography Module**: Für sichere Key Derivation Functions
- **SessionId/SessionIdGenerator**: Für Session-Management
- **IpAddress Value Object**: Für IP-basierte Sicherheitsfeatures
- **Hash Value Object**: Für typisierte Hash-Operationen
### Repository Pattern
Flexible Datenpersistierung durch Repository-Abstraktion.
```php
interface AuthenticationRepository
{
public function findUserByIdentifier(string $identifier): ?User;
public function findUserById(string $userId): ?User;
public function updateUserPassword(string $userId, HashedPassword $password): bool;
public function storeSession(AuthenticationSession $session): void;
public function findSessionById(SessionId $sessionId): ?AuthenticationSession;
public function updateSessionActivity(SessionId $sessionId, ?IpAddress $ipAddress): void;
public function deleteSession(SessionId $sessionId): bool;
public function deleteAllUserSessions(string $userId): void;
public function storeRememberToken(RememberToken $token): void;
public function findRememberToken(Hash $tokenHash): ?RememberToken;
public function deleteRememberToken(Hash $tokenHash): bool;
public function deleteAllUserRememberTokens(string $userId): void;
public function getFailedLoginAttempts(string $userId): int;
public function incrementFailedLoginAttempts(string $userId): void;
public function clearFailedLoginAttempts(string $userId): void;
public function getLastFailedAttemptTime(string $userId): ?\DateTimeImmutable;
}
```
## Best Practices
### Sichere Implementation
```php
// ✅ Sensitive Parameter verwenden
public function authenticate(
string $identifier,
#[SensitiveParameter] string $password,
?IpAddress $ipAddress = null
): AuthenticationResult
// ✅ Automatisches Rehashing
if ($this->passwordHasher->needsRehash($user->getHashedPassword())) {
$newHash = $this->passwordHasher->hash($password);
$this->repository->updateUserPassword($user->getId(), $newHash);
}
// ✅ Timing-safe Token-Vergleiche
$tokenHash = $this->hashToken($tokenValue);
$rememberToken = $this->repository->findRememberToken($tokenHash);
```
### Error Handling
```php
// ✅ Generische Fehlermeldungen für Sicherheit
if (!$user || !$this->passwordHasher->verify($password, $user->getHashedPassword())) {
return AuthenticationResult::failed('Invalid credentials');
}
// ✅ Security Event Logging
$this->recordSecurityEvent('authentication_failed', [
'identifier' => $identifier,
'ip_address' => $ipAddress ? (string) $ipAddress : null,
'reason' => $reason
]);
```
### Performance Optimierung
```php
// ✅ Konfigurierbare Sicherheitslevel
$passwordHasher = new PasswordHasher(
kdf: $keyDerivationFunction,
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_STANDARD // vs HIGH für höhere Sicherheit
);
// ✅ Bulk-Operationen für bessere Performance
$this->repository->deleteAllUserSessions($userId);
$this->repository->deleteAllUserRememberTokens($userId);
```
## Nächste Schritte
- [ ] AuthenticationRepository Interface implementieren
- [ ] Authentication Exceptions definieren
- [ ] Unit Tests für alle Komponenten schreiben
- [ ] Rate Limiting Service implementieren
- [ ] Integration Tests für Authentication Flow
- [ ] Performance Benchmarks für Passwort-Hashing

View File

@@ -0,0 +1,904 @@
# Auth Module Security Guidelines
**Sicherheitsrichtlinien und Best Practices** für das Auth Module des Custom PHP Frameworks.
## Security Architecture
### Defense in Depth Strategy
Das Auth Module implementiert mehrschichtige Sicherheitsmaßnahmen:
```
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ • Rate Limiting & Account Lockout │
│ • Session Security & IP Tracking │
│ • Multi-Factor Authentication │
│ • Password Strength Validation │
├─────────────────────────────────────────────────────────────┤
│ Framework Layer │
├─────────────────────────────────────────────────────────────┤
│ • Secure Password Hashing (Argon2ID) │
│ • Timing-Safe Comparisons │
│ • Cryptographically Secure Random Generation │
│ • Hash Value Object with Algorithm Validation │
├─────────────────────────────────────────────────────────────┤
│ Infrastructure Layer │
├─────────────────────────────────────────────────────────────┤
│ • HTTPS Enforcement │
│ • Secure Headers (HSTS, CSP) │
│ • Database Security & Prepared Statements │
│ • Logging & Monitoring │
└─────────────────────────────────────────────────────────────┘
```
## Password Security
### Secure Password Hashing
```php
// ✅ Recommended: Argon2ID with high memory cost
$passwordHasher = new PasswordHasher(
kdf: $keyDerivationFunction,
defaultAlgorithm: 'argon2id',
defaultSecurityLevel: PasswordHasher::LEVEL_HIGH // Production
);
// Security parameters for different levels:
// LEVEL_HIGH: 128 MB memory, 6 iterations, 4 threads
// LEVEL_STANDARD: 64 MB memory, 4 iterations, 3 threads
// LEVEL_LOW: 32 MB memory, 2 iterations, 2 threads
// ❌ Avoid: Weak algorithms or low security levels in production
$weakHasher = new PasswordHasher(
kdf: $kdf,
defaultAlgorithm: 'pbkdf2-sha256', // Less secure than Argon2ID
defaultSecurityLevel: PasswordHasher::LEVEL_LOW
);
```
### Password Policy Enforcement
```php
final readonly class PasswordPolicy
{
public const int MIN_LENGTH = 12; // Increased from 8
public const int MIN_COMPLEXITY_TYPES = 3; // Upper, lower, numbers, special
public const int MIN_STRENGTH_SCORE = 70; // High security threshold
public function validatePassword(string $password): PasswordPolicyResult
{
$violations = [];
// Length requirement
if (mb_strlen($password) < self::MIN_LENGTH) {
$violations[] = "Password must be at least " . self::MIN_LENGTH . " characters";
}
// Complexity requirements
$complexityScore = $this->calculateComplexity($password);
if ($complexityScore < self::MIN_COMPLEXITY_TYPES) {
$violations[] = "Password must include at least " . self::MIN_COMPLEXITY_TYPES . " character types";
}
// Entropy check
if ($this->calculateEntropy($password) < 50) {
$violations[] = "Password lacks sufficient entropy";
}
// Dictionary check
if ($this->isCommonPassword($password)) {
$violations[] = "Password is too common";
}
// Personal information check
if ($this->containsPersonalInfo($password)) {
$violations[] = "Password should not contain personal information";
}
return new PasswordPolicyResult(
isValid: empty($violations),
violations: $violations,
strengthScore: $this->calculateStrengthScore($password)
);
}
private function calculateEntropy(string $password): float
{
$length = strlen($password);
$charsetSize = 0;
if (preg_match('/[a-z]/', $password)) $charsetSize += 26;
if (preg_match('/[A-Z]/', $password)) $charsetSize += 26;
if (preg_match('/[0-9]/', $password)) $charsetSize += 10;
if (preg_match('/[^A-Za-z0-9]/', $password)) $charsetSize += 32;
return $length * log($charsetSize, 2);
}
private function isCommonPassword(string $password): bool
{
// Check against common password lists (e.g., Have I Been Pwned)
$commonPasswords = [
'password', '123456', 'password123', 'admin', 'qwerty',
'letmein', 'welcome', 'monkey', 'dragon', '123456789'
];
return in_array(strtolower($password), $commonPasswords, true);
}
}
```
### Automatic Password Rehashing
```php
// ✅ Implement automatic rehashing on authentication
public function authenticate(string $identifier, string $password): AuthenticationResult
{
$user = $this->repository->findByIdentifier($identifier);
if (!$user || !$this->passwordHasher->verify($password, $user->getHashedPassword())) {
return AuthenticationResult::failed('Invalid credentials');
}
// Critical: Check for rehashing need
if ($this->passwordHasher->needsRehash($user->getHashedPassword())) {
$newHash = $this->passwordHasher->hash($password);
$this->repository->updateUserPassword($user->getId(), $newHash);
$this->logger->info('Password automatically rehashed', [
'user_id' => $user->getId(),
'old_algorithm' => $user->getHashedPassword()->getAlgorithm(),
'new_algorithm' => $newHash->getAlgorithm()
]);
}
return AuthenticationResult::success($user);
}
```
## Session Security
### Secure Session Management
```php
final readonly class SecureSessionManager
{
public function __construct(
private AuthenticationService $authService,
private SessionSecurity $sessionSecurity
) {}
public function createSecureSession(
User $user,
IpAddress $ipAddress,
UserAgent $userAgent
): AuthenticationSession {
// Generate cryptographically secure session ID
$sessionId = $this->generateSecureSessionId();
// Create session with security attributes
$session = new AuthenticationSession(
id: $sessionId,
userId: $user->getId(),
ipAddress: $ipAddress,
userAgent: $userAgent,
createdAt: new \DateTimeImmutable(),
expiresAt: new \DateTimeImmutable('+1 hour'),
lastActivity: new \DateTimeImmutable(),
securityAttributes: new SessionSecurityAttributes(
isSecure: true,
isHttpOnly: true,
sameSite: 'Strict',
fingerprint: $this->generateFingerprint($ipAddress, $userAgent)
)
);
// Set secure session cookie
$this->setSecureSessionCookie($sessionId, $session->getExpiresAt());
return $session;
}
private function generateSecureSessionId(): SessionId
{
// Use framework's SessionIdGenerator for consistency
return $this->sessionIdGenerator->generate();
}
private function setSecureSessionCookie(SessionId $sessionId, \DateTimeImmutable $expiresAt): void
{
setcookie('session_id', $sessionId->toString(), [
'expires' => $expiresAt->getTimestamp(),
'path' => '/',
'domain' => '', // Let browser determine
'secure' => true, // HTTPS only
'httponly' => true, // No JavaScript access
'samesite' => 'Strict' // CSRF protection
]);
}
private function generateFingerprint(IpAddress $ipAddress, UserAgent $userAgent): string
{
return Hash::sha256(
$ipAddress->toString() . '|' .
$userAgent->toString() . '|' .
$_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
)->toString();
}
}
```
### Session Hijacking Protection
```php
final readonly class SessionHijackingProtection
{
public function validateSession(
SessionId $sessionId,
IpAddress $currentIp,
UserAgent $currentUserAgent
): SessionValidationResult {
$session = $this->repository->findSessionById($sessionId);
if (!$session) {
return SessionValidationResult::invalid('Session not found');
}
// Check session expiration
if ($session->isExpired()) {
$this->repository->deleteSession($sessionId);
return SessionValidationResult::expired();
}
// IP address consistency check (configurable)
if ($this->config->checkIpConsistency) {
if (!$session->getIpAddress()->equals($currentIp)) {
$this->logSecurityEvent('session_ip_mismatch', [
'session_id' => $sessionId->toString(),
'original_ip' => (string) $session->getIpAddress(),
'current_ip' => (string) $currentIp
]);
// Suspicious activity - invalidate session
$this->repository->deleteSession($sessionId);
return SessionValidationResult::suspicious('IP address mismatch');
}
}
// User Agent consistency (less strict)
if (!$this->isUserAgentConsistent($session->getUserAgent(), $currentUserAgent)) {
$this->logSecurityEvent('session_user_agent_change', [
'session_id' => $sessionId->toString(),
'original_ua' => (string) $session->getUserAgent(),
'current_ua' => (string) $currentUserAgent
]);
}
// Update session activity
$this->repository->updateSessionActivity($sessionId, $currentIp);
return SessionValidationResult::valid($session);
}
private function isUserAgentConsistent(UserAgent $original, UserAgent $current): bool
{
// Allow minor version changes but detect major browser changes
$originalBrowser = $this->extractBrowser($original);
$currentBrowser = $this->extractBrowser($current);
return $originalBrowser === $currentBrowser;
}
}
```
## Token Security
### Remember Token Security with Hash Value Object
```php
final readonly class RememberTokenSecurity
{
public function createRememberToken(string $userId): RememberToken
{
// Generate cryptographically secure token
$tokenValue = bin2hex(random_bytes(32)); // 256-bit token
// Hash token using Hash Value Object for type safety
$tokenHash = Hash::sha256($tokenValue);
$rememberToken = new RememberToken(
hash: $tokenHash,
userId: $userId,
createdAt: new \DateTimeImmutable(),
expiresAt: new \DateTimeImmutable('+30 days'),
lastUsed: null
);
$this->repository->storeRememberToken($rememberToken);
// Return token with plain text value (only shown once)
return $rememberToken->withPlainTextValue($tokenValue);
}
public function validateRememberToken(string $tokenValue): RememberTokenValidation
{
// Hash provided token for comparison
$tokenHash = Hash::sha256($tokenValue);
$rememberToken = $this->repository->findRememberToken($tokenHash);
if (!$rememberToken) {
$this->logSecurityEvent('invalid_remember_token', [
'token_hash' => $tokenHash->toShort(8) // Only log first 8 chars
]);
return RememberTokenValidation::invalid();
}
// Check expiration
if ($rememberToken->isExpired()) {
$this->repository->deleteRememberToken($tokenHash);
return RememberTokenValidation::expired();
}
// Update last used timestamp
$this->repository->updateRememberTokenUsage($tokenHash);
return RememberTokenValidation::valid($rememberToken);
}
public function rotateRememberToken(Hash $oldTokenHash): RememberToken
{
$oldToken = $this->repository->findRememberToken($oldTokenHash);
if (!$oldToken) {
throw new SecurityException('Cannot rotate non-existent token');
}
// Create new token
$newToken = $this->createRememberToken($oldToken->getUserId());
// Delete old token
$this->repository->deleteRememberToken($oldTokenHash);
$this->logSecurityEvent('remember_token_rotated', [
'user_id' => $oldToken->getUserId(),
'old_token_created' => $oldToken->getCreatedAt()->format('Y-m-d H:i:s')
]);
return $newToken;
}
}
// Benefits of using Hash Value Object:
// ✅ Type safety - tokens can't be confused with other strings
// ✅ Algorithm specification - explicit SHA-256 usage
// ✅ Timing-safe comparison - uses hash_equals() internally
// ✅ Validation - ensures proper hash format and length
// ✅ Framework consistency - follows value object patterns
```
## Rate Limiting & Brute Force Protection
### Advanced Rate Limiting
```php
final readonly class AdvancedRateLimiter implements RateLimitService
{
public function __construct(
private Cache $cache,
private Logger $logger,
private RateLimitConfig $config
) {}
public function checkRateLimit(
IpAddress $ipAddress,
string $action,
string $identifier = null
): RateLimitResult {
// Multiple rate limiting strategies
$checks = [
$this->checkIpBasedLimit($ipAddress, $action),
$this->checkIdentifierBasedLimit($identifier, $action),
$this->checkGlobalLimit($action),
$this->checkAdaptiveLimit($ipAddress, $action)
];
foreach ($checks as $check) {
if ($check->isLimited()) {
$this->logRateLimitViolation($ipAddress, $action, $check);
return $check;
}
}
// Record successful attempt
$this->recordAttempt($ipAddress, $action, $identifier);
return RateLimitResult::allowed();
}
private function checkAdaptiveLimit(IpAddress $ipAddress, string $action): RateLimitResult
{
$suspiciousScore = $this->calculateSuspiciousScore($ipAddress);
// Adaptive limits based on suspicious activity
$maxAttempts = match (true) {
$suspiciousScore > 80 => 1, // Highly suspicious
$suspiciousScore > 60 => 2, // Suspicious
$suspiciousScore > 40 => 3, // Moderately suspicious
default => 5 // Normal
};
$key = "adaptive_limit:{$action}:" . $ipAddress->toString();
$attempts = $this->cache->get($key, 0);
if ($attempts >= $maxAttempts) {
return RateLimitResult::limited(
reason: 'Adaptive rate limit exceeded',
retryAfter: 900,
metadata: ['suspicious_score' => $suspiciousScore]
);
}
return RateLimitResult::allowed();
}
private function calculateSuspiciousScore(IpAddress $ipAddress): int
{
$score = 0;
// Check for various suspicious indicators
if ($this->isFromTorNetwork($ipAddress)) $score += 30;
if ($this->isFromVpnProvider($ipAddress)) $score += 15;
if ($this->hasRecentFailures($ipAddress)) $score += 20;
if ($this->isFromUncommonGeolocation($ipAddress)) $score += 10;
if ($this->hasRapidRequests($ipAddress)) $score += 25;
return min(100, $score);
}
private function logRateLimitViolation(
IpAddress $ipAddress,
string $action,
RateLimitResult $result
): void {
$this->logger->warning('Rate limit violation', [
'ip_address' => (string) $ipAddress,
'action' => $action,
'reason' => $result->getReason(),
'retry_after' => $result->getRetryAfter(),
'metadata' => $result->getMetadata()
]);
}
}
```
### Account Lockout Protection
```php
final readonly class AccountLockoutService
{
public function __construct(
private UserRepository $repository,
private Logger $logger,
private NotificationService $notifications
) {}
public function handleFailedLogin(string $identifier, IpAddress $ipAddress): void
{
$user = $this->repository->findByIdentifier($identifier);
if (!$user) {
return; // Don't reveal if user exists
}
$attempts = $this->repository->incrementFailedAttempts($user->getId());
// Progressive lockout strategy
$lockoutDuration = $this->calculateLockoutDuration($attempts);
if ($attempts >= 3) {
$this->repository->lockAccount($user->getId(), $lockoutDuration);
$this->logger->warning('Account locked due to failed attempts', [
'user_id' => $user->getId(),
'attempts' => $attempts,
'lockout_duration' => $lockoutDuration,
'ip_address' => (string) $ipAddress
]);
// Notify user of lockout (rate limited to prevent abuse)
$this->sendLockoutNotification($user, $attempts, $lockoutDuration);
}
}
private function calculateLockoutDuration(int $attempts): int
{
// Exponential backoff with jitter
return match (true) {
$attempts >= 10 => 3600 + random_int(0, 1800), // 1-2.5 hours
$attempts >= 7 => 1800 + random_int(0, 900), // 30-45 minutes
$attempts >= 5 => 900 + random_int(0, 300), // 15-20 minutes
$attempts >= 3 => 300 + random_int(0, 120), // 5-7 minutes
default => 0
};
}
private function sendLockoutNotification(
User $user,
int $attempts,
int $lockoutDuration
): void {
// Rate limit notifications to prevent spam
$notificationKey = "lockout_notification:" . $user->getId();
if ($this->cache->has($notificationKey)) {
return;
}
$this->cache->put($notificationKey, true, 300); // 5 minutes
$this->notifications->sendSecurityAlert($user->getEmail(), [
'type' => 'account_lockout',
'attempts' => $attempts,
'lockout_duration_minutes' => ceil($lockoutDuration / 60),
'timestamp' => new \DateTimeImmutable()
]);
}
}
```
## Multi-Factor Authentication Security
### TOTP Implementation with Security Features
```php
final readonly class SecureTotpService
{
private const int SECRET_LENGTH = 32; // 160-bit secret
private const int WINDOW_SIZE = 1; // Allow 1 time step tolerance
private const int TIME_STEP = 30; // 30-second time steps
public function generateSecret(): string
{
return Base32::encode(random_bytes(self::SECRET_LENGTH));
}
public function verify(string $code, string $secret, ?int $timestamp = null): bool
{
$timestamp = $timestamp ?? time();
$timeStep = intval($timestamp / self::TIME_STEP);
// Check current and adjacent time windows to account for clock drift
for ($i = -self::WINDOW_SIZE; $i <= self::WINDOW_SIZE; $i++) {
$calculatedCode = $this->calculateTotp($secret, $timeStep + $i);
if (hash_equals($code, $calculatedCode)) {
// Prevent replay attacks by storing used codes
$this->markCodeAsUsed($secret, $timeStep + $i);
return true;
}
}
return false;
}
private function calculateTotp(string $secret, int $timeStep): string
{
$binarySecret = Base32::decode($secret);
$timeBytes = pack('N*', 0) . pack('N*', $timeStep);
$hash = hash_hmac('sha1', $timeBytes, $binarySecret, true);
$offset = ord($hash[19]) & 0xf;
$code = (
((ord($hash[$offset]) & 0x7f) << 24) |
((ord($hash[$offset + 1]) & 0xff) << 16) |
((ord($hash[$offset + 2]) & 0xff) << 8) |
(ord($hash[$offset + 3]) & 0xff)
) % 1000000;
return str_pad((string) $code, 6, '0', STR_PAD_LEFT);
}
private function markCodeAsUsed(string $secret, int $timeStep): void
{
// Prevent replay attacks
$key = 'totp_used:' . Hash::sha256($secret . ':' . $timeStep)->toShort(16);
$this->cache->put($key, true, self::TIME_STEP * 2);
}
}
```
### Backup Code Security
```php
final readonly class BackupCodeService
{
private const int CODE_LENGTH = 8;
private const int CODE_COUNT = 8;
public function generateBackupCodes(string $userId): array
{
$codes = [];
for ($i = 0; $i < self::CODE_COUNT; $i++) {
$code = $this->generateHumanReadableCode();
$codes[] = $code;
// Store hashed version
$backupCode = new MfaBackupCode(
userId: $userId,
codeHash: Hash::sha256($code),
createdAt: new \DateTimeImmutable(),
usedAt: null
);
$this->repository->storeBackupCode($backupCode);
}
return $codes;
}
public function verifyBackupCode(string $userId, string $code): bool
{
$codeHash = Hash::sha256($code);
$backupCode = $this->repository->findBackupCode($userId, $codeHash);
if (!$backupCode || $backupCode->isUsed()) {
return false;
}
// Mark as used (one-time use only)
$this->repository->markBackupCodeAsUsed($backupCode, new \DateTimeImmutable());
return true;
}
private function generateHumanReadableCode(): string
{
// Generate readable codes avoiding confusing characters
$chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // No 0, 1, I, O
$code = '';
for ($i = 0; $i < self::CODE_LENGTH; $i++) {
$code .= $chars[random_int(0, strlen($chars) - 1)];
}
// Format as XXXX-XXXX for readability
return substr($code, 0, 4) . '-' . substr($code, 4, 4);
}
}
```
## Monitoring & Incident Response
### Security Event Monitoring
```php
final readonly class SecurityEventMonitor
{
public function __construct(
private Logger $securityLogger,
private AlertingService $alerting,
private MetricsCollector $metrics
) {}
public function recordSecurityEvent(SecurityEvent $event): void
{
// Log all security events
$this->securityLogger->warning('Security event recorded', [
'event_type' => $event->getType(),
'severity' => $event->getSeverity(),
'user_id' => $event->getUserId(),
'ip_address' => (string) $event->getIpAddress(),
'user_agent' => (string) $event->getUserAgent(),
'metadata' => $event->getMetadata(),
'timestamp' => $event->getTimestamp()->format('Y-m-d H:i:s')
]);
// Update security metrics
$this->metrics->increment('auth.security_events', [
'type' => $event->getType(),
'severity' => $event->getSeverity()
]);
// Send alerts for critical events
if ($event->isCritical()) {
$this->alerting->sendSecurityAlert($event);
}
// Check for attack patterns
$this->analyzeForAttackPatterns($event);
}
private function analyzeForAttackPatterns(SecurityEvent $event): void
{
$ipAddress = $event->getIpAddress();
$recentEvents = $this->getRecentSecurityEvents($ipAddress, minutes: 10);
// Detect brute force attacks
$failedLogins = array_filter($recentEvents,
fn($e) => $e->getType() === 'authentication_failed'
);
if (count($failedLogins) >= 5) {
$this->alerting->sendAlert(new BruteForceDetectionAlert(
ipAddress: $ipAddress,
attemptCount: count($failedLogins),
timeWindow: 10
));
}
// Detect credential stuffing
$uniqueIdentifiers = array_unique(array_map(
fn($e) => $e->getMetadata()['identifier'] ?? null,
$failedLogins
));
if (count($uniqueIdentifiers) >= 10) {
$this->alerting->sendAlert(new CredentialStuffingAlert(
ipAddress: $ipAddress,
uniqueIdentifiers: count($uniqueIdentifiers)
));
}
}
}
```
### Automated Response System
```php
final readonly class AutomatedSecurityResponse
{
public function handleSecurityEvent(SecurityEvent $event): void
{
match ($event->getType()) {
'brute_force_detected' => $this->handleBruteForce($event),
'credential_stuffing_detected' => $this->handleCredentialStuffing($event),
'session_hijacking_suspected' => $this->handleSessionHijacking($event),
'suspicious_login_pattern' => $this->handleSuspiciousLogin($event),
default => null
};
}
private function handleBruteForce(SecurityEvent $event): void
{
$ipAddress = $event->getIpAddress();
// Immediately block IP for 1 hour
$this->firewall->blockIpAddress($ipAddress, duration: 3600);
// Increase monitoring for this IP
$this->monitoring->increaseWatchLevel($ipAddress);
// Notify security team
$this->alerting->sendUrgentAlert(new BruteForceBlockAlert($ipAddress));
}
private function handleSessionHijacking(SecurityEvent $event): void
{
$userId = $event->getUserId();
$sessionId = $event->getMetadata()['session_id'];
// Immediately terminate all user sessions
$this->authService->logoutAll($userId);
// Require password reset
$this->userService->requirePasswordReset($userId);
// Send security notification to user
$user = $this->userRepository->findById($userId);
$this->notifications->sendSecurityBreach($user->getEmail(), [
'incident_type' => 'session_hijacking',
'affected_session' => $sessionId,
'timestamp' => $event->getTimestamp()
]);
}
}
```
## Compliance & Audit
### GDPR Compliance
```php
final readonly class GdprCompliantAuthService
{
public function processPersonalData(User $user, string $purpose): void
{
// Log data processing for GDPR audit trail
$this->auditLogger->info('Personal data processed', [
'user_id' => $user->getId(),
'data_types' => ['email', 'login_timestamps', 'ip_addresses'],
'processing_purpose' => $purpose,
'legal_basis' => 'legitimate_interest', // or 'consent'
'retention_period' => '2_years'
]);
}
public function exportUserData(string $userId): UserDataExport
{
$user = $this->repository->findById($userId);
if (!$user) {
throw new UserNotFoundException($userId);
}
return new UserDataExport([
'personal_data' => [
'user_id' => $user->getId(),
'email' => (string) $user->getEmail(),
'created_at' => $user->getCreatedAt()->format('c'),
'last_login' => $user->getLastLoginAt()?->format('c')
],
'authentication_data' => [
'password_last_changed' => $user->getPasswordChangedAt()?->format('c'),
'mfa_enabled' => $user->hasMfaEnabled(),
'failed_login_attempts' => $this->repository->getFailedLoginAttempts($userId)
],
'session_data' => $this->getSessionHistory($userId),
'security_events' => $this->getSecurityEventHistory($userId)
]);
}
public function deleteUserData(string $userId): void
{
// GDPR right to erasure (right to be forgotten)
$this->repository->anonymizeUser($userId);
$this->repository->deleteAllUserSessions($userId);
$this->repository->deleteAllUserRememberTokens($userId);
$this->repository->deleteUserSecurityEvents($userId);
$this->auditLogger->info('User data deleted per GDPR request', [
'user_id' => $userId,
'deletion_timestamp' => (new \DateTimeImmutable())->format('c')
]);
}
}
```
### Security Audit Logging
```php
final readonly class SecurityAuditLogger
{
public function logAuthenticationEvent(string $eventType, array $context): void
{
$auditRecord = [
'event_type' => $eventType,
'timestamp' => (new \DateTimeImmutable())->format('c'),
'user_id' => $context['user_id'] ?? null,
'session_id' => $context['session_id'] ?? null,
'ip_address' => $context['ip_address'] ?? null,
'user_agent' => $context['user_agent'] ?? null,
'success' => $context['success'] ?? false,
'failure_reason' => $context['failure_reason'] ?? null,
'risk_score' => $this->calculateRiskScore($context)
];
// Store in secure audit log (append-only, tamper-evident)
$this->auditStorage->append($auditRecord);
// Forward to SIEM if configured
if ($this->siemForwarder) {
$this->siemForwarder->forward($auditRecord);
}
}
private function calculateRiskScore(array $context): int
{
$score = 0;
// Location-based risk
if (isset($context['ip_address'])) {
$location = $this->geolocator->locate($context['ip_address']);
if ($location->isHighRiskCountry()) $score += 25;
if ($location->isFromTor()) $score += 50;
}
// Time-based risk
$hour = (int) date('H');
if ($hour < 6 || $hour > 22) $score += 10; // Outside business hours
// Failure patterns
if (!($context['success'] ?? true)) $score += 20;
return min(100, $score);
}
}

View File

@@ -0,0 +1,397 @@
# Cryptography Module - Konfiguration
Konfigurationsoptionen und Setup für das Cryptography Module.
## Service-Registrierung
### Dependency Injection Setup
```php
use App\Framework\DI\Initializer;
use App\Framework\DI\Container;
use App\Framework\Cryptography\KeyDerivationFunction;
use App\Framework\Cryptography\SecureTokenGenerator;
use App\Framework\Cryptography\CryptographicUtilities;
use App\Framework\Cryptography\DigitalSignature;
use App\Framework\Cryptography\AdvancedHash;
use App\Framework\Random\SecureRandomGenerator;
final readonly class CryptographyInitializer implements Initializer
{
public function initialize(Container $container): void
{
// Random Generator als Dependency
$container->singleton(RandomGenerator::class, new SecureRandomGenerator());
// Core Cryptography Services
$container->singleton(KeyDerivationFunction::class, function(Container $c) {
return new KeyDerivationFunction($c->get(RandomGenerator::class));
});
$container->singleton(SecureTokenGenerator::class, function(Container $c) {
return new SecureTokenGenerator($c->get(RandomGenerator::class));
});
$container->singleton(CryptographicUtilities::class, function(Container $c) {
return new CryptographicUtilities($c->get(RandomGenerator::class));
});
$container->singleton(DigitalSignature::class, function(Container $c) {
return new DigitalSignature($c->get(RandomGenerator::class));
});
$container->singleton(AdvancedHash::class, new AdvancedHash());
// Factory Methods als Alternative
$container->bind('kdf.factory', function(Container $c) {
return KeyDerivationFunction::create($c->get(RandomGenerator::class));
});
}
}
```
### AppBootstrapper Integration
```php
// In src/Framework/Core/AppBootstrapper.php
public function bootstrap(): Application
{
// ... existing bootstrapping
$initializer = new CryptographyInitializer();
$initializer->initialize($this->container);
return $application;
}
```
## Sicherheitsparameter
### Key Derivation Function Konfiguration
```php
final readonly class CryptographyConfig
{
public const DEFAULT_KDF_PARAMETERS = [
'pbkdf2-sha256' => [
'low' => ['iterations' => 50000, 'key_length' => 32],
'standard' => ['iterations' => 100000, 'key_length' => 32],
'high' => ['iterations' => 200000, 'key_length' => 32],
],
'argon2id' => [
'low' => ['memory_cost' => 32768, 'time_cost' => 3, 'threads' => 2],
'standard' => ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 3],
'high' => ['memory_cost' => 131072, 'time_cost' => 6, 'threads' => 4],
],
'scrypt' => [
'low' => ['cost_parameter' => 8192, 'block_size' => 8, 'parallelization' => 1],
'standard' => ['cost_parameter' => 16384, 'block_size' => 8, 'parallelization' => 1],
'high' => ['cost_parameter' => 32768, 'block_size' => 8, 'parallelization' => 2],
]
];
public static function getRecommendedParameters(
string $algorithm,
string $securityLevel = 'standard'
): array {
return self::DEFAULT_KDF_PARAMETERS[$algorithm][$securityLevel] ??
throw new InvalidArgumentException("Unsupported configuration: {$algorithm}:{$securityLevel}");
}
}
```
### Token-Generator Konfiguration
```php
final readonly class TokenConfig
{
public const DEFAULT_TOKEN_LENGTHS = [
SecureTokenGenerator::TYPE_API_KEY => 32, // 256 Bit
SecureTokenGenerator::TYPE_SESSION => 32, // 256 Bit
SecureTokenGenerator::TYPE_CSRF => 32, // 256 Bit
SecureTokenGenerator::TYPE_REFRESH => 64, // 512 Bit
SecureTokenGenerator::TYPE_VERIFICATION => 32, // 256 Bit
SecureTokenGenerator::TYPE_BEARER => 32, // 256 Bit
SecureTokenGenerator::TYPE_WEBHOOK => 32, // 256 Bit
];
public const DEFAULT_TOKEN_FORMATS = [
SecureTokenGenerator::TYPE_API_KEY => SecureTokenGenerator::FORMAT_BASE64_URL,
SecureTokenGenerator::TYPE_SESSION => SecureTokenGenerator::FORMAT_BASE64_URL,
SecureTokenGenerator::TYPE_OTP => SecureTokenGenerator::FORMAT_NUMERIC,
];
public const TOKEN_EXPIRY_TIMES = [
SecureTokenGenerator::TYPE_SESSION => 3600 * 24, // 24 Stunden
SecureTokenGenerator::TYPE_CSRF => 3600, // 1 Stunde
SecureTokenGenerator::TYPE_VERIFICATION => 3600 * 2, // 2 Stunden
SecureTokenGenerator::TYPE_OTP => 300, // 5 Minuten
SecureTokenGenerator::TYPE_REFRESH => 3600 * 24 * 30, // 30 Tage
];
}
```
## Environment-basierte Konfiguration
### .env Konfiguration
```bash
# Cryptography Configuration
CRYPTO_DEFAULT_KDF_ALGORITHM=argon2id
CRYPTO_DEFAULT_SECURITY_LEVEL=standard
# Token Configuration
CRYPTO_DEFAULT_TOKEN_LENGTH=32
CRYPTO_DEFAULT_TOKEN_FORMAT=base64_url
# Digital Signature Configuration
CRYPTO_DEFAULT_RSA_KEY_SIZE=2048
CRYPTO_DEFAULT_EC_CURVE=secp256r1
# Performance Configuration
CRYPTO_BATCH_SIZE_LIMIT=1000
CRYPTO_ENTROPY_THRESHOLD=7.0
```
### Environment-aware Service
```php
use App\Framework\Core\Environment;
use App\Framework\Core\EnvKey;
final readonly class CryptographyConfigService
{
public function __construct(
private Environment $environment
) {}
public function getDefaultKdfAlgorithm(): string
{
return $this->environment->get(
EnvKey::from('CRYPTO_DEFAULT_KDF_ALGORITHM'),
'argon2id'
);
}
public function getDefaultSecurityLevel(): string
{
return $this->environment->get(
EnvKey::from('CRYPTO_DEFAULT_SECURITY_LEVEL'),
'standard'
);
}
public function getDefaultTokenLength(): int
{
return $this->environment->getInt(
EnvKey::from('CRYPTO_DEFAULT_TOKEN_LENGTH'),
32
);
}
public function getEntropyThreshold(): float
{
return $this->environment->getFloat(
EnvKey::from('CRYPTO_ENTROPY_THRESHOLD'),
7.0
);
}
}
```
## Advanced Configuration
### Custom RandomGenerator
```php
// Eigener RandomGenerator für spezielle Anforderungen
final readonly class CustomSecureRandomGenerator implements RandomGenerator
{
public function bytes(int $length): string
{
// Custom implementation mit Hardware-RNG
return $this->getHardwareRandomBytes($length);
}
public function int(int $min = 0, int $max = PHP_INT_MAX): int
{
return random_int($min, $max);
}
private function getHardwareRandomBytes(int $length): string
{
// Implementation für Hardware-RNG
// z.B. über /dev/hwrng oder externe HSM
}
}
// In Initializer registrieren
$container->singleton(RandomGenerator::class, new CustomSecureRandomGenerator());
```
### Performance-optimierte Konfiguration
```php
final readonly class PerformanceOptimizedCryptoInitializer implements Initializer
{
public function initialize(Container $container): void
{
// Cached Services für bessere Performance
$container->singleton(KeyDerivationFunction::class, function(Container $c) {
$kdf = new KeyDerivationFunction($c->get(RandomGenerator::class));
return new CachedKeyDerivationFunction($kdf, $c->get(Cache::class));
});
// Batch-optimierte Token-Generierung
$container->singleton(SecureTokenGenerator::class, function(Container $c) {
return new BatchOptimizedTokenGenerator(
$c->get(RandomGenerator::class),
batchSize: 100 // Tokens in Batches vorgenerieren
);
});
}
}
```
### Test-Konfiguration
```php
// Für Unit Tests - deterministic aber sichere Werte
final readonly class TestCryptographyInitializer implements Initializer
{
public function initialize(Container $container): void
{
if (!$this->isTestEnvironment()) {
throw new InvalidArgumentException('TestCryptographyInitializer nur in Test-Umgebung verwenden');
}
// Deterministischer aber sicherer Generator für Tests
$container->singleton(RandomGenerator::class, new TestRandomGenerator());
// Reduzierte Parameter für schnellere Tests
$container->singleton(KeyDerivationFunction::class, function(Container $c) {
return new KeyDerivationFunction($c->get(RandomGenerator::class));
});
}
private function isTestEnvironment(): bool
{
return defined('PHPUNIT_RUNNING') ||
(isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing');
}
}
```
## Middleware-Integration
### Automatic Token Validation
```php
final readonly class CryptoTokenValidationMiddleware
{
public function __construct(
private CryptographicUtilities $utils,
private CacheInterface $cache
) {}
public function handle(HttpRequest $request, RequestHandler $handler): HttpResponse
{
$authHeader = $request->server->getAuthorizationHeader();
if ($authHeader && str_starts_with($authHeader, 'Bearer ')) {
$token = substr($authHeader, 7);
// Token-Format validieren
if (!$this->utils->timingSafeEquals($token, trim($token))) {
throw new AuthenticationException('Invalid token format');
}
// Cache für Token-Validierung
$cacheKey = 'token_valid_' . hash('sha256', $token);
if (!$this->cache->has($cacheKey)) {
// Token in Datenbank validieren
$isValid = $this->validateTokenInDatabase($token);
$this->cache->set($cacheKey, $isValid, 300); // 5 Min Cache
}
}
return $handler->handle($request);
}
}
```
## Monitoring und Logging
### Security Event Integration
```php
final readonly class CryptographyEventLogger
{
public function __construct(
private Logger $logger,
private SecurityEventLogger $securityLogger
) {}
public function logTokenGeneration(SecureToken $token): void
{
$this->logger->info('Token generated', [
'type' => $token->getType(),
'format' => $token->getFormat(),
'length' => $token->getLength(),
'fingerprint' => $token->getShortFingerprint()
]);
}
public function logSuspiciousTokenActivity(string $token, string $reason): void
{
$this->securityLogger->logSecurityEvent(
new SuspiciousTokenActivityEvent(
tokenFingerprint: hash('sha256', $token),
reason: $reason,
timestamp: new DateTimeImmutable()
)
);
}
}
```
## Validierung der Konfiguration
```php
final readonly class CryptographyConfigValidator
{
public static function validateConfiguration(Container $container): void
{
// RandomGenerator verfügbar?
if (!$container->has(RandomGenerator::class)) {
throw new ConfigurationException('RandomGenerator nicht registriert');
}
// Cryptography Services verfügbar?
$requiredServices = [
KeyDerivationFunction::class,
SecureTokenGenerator::class,
CryptographicUtilities::class
];
foreach ($requiredServices as $service) {
if (!$container->has($service)) {
throw new ConfigurationException("Service nicht registriert: {$service}");
}
}
// Sicherheitsparameter validieren
$kdf = $container->get(KeyDerivationFunction::class);
try {
$params = $kdf->getRecommendedParameters('pbkdf2-sha256', 'standard');
if ($params['iterations'] < 50000) {
throw new ConfigurationException('PBKDF2 Iterationen zu niedrig (Minimum: 50000)');
}
} catch (InvalidArgumentException $e) {
throw new ConfigurationException('Ungültige KDF-Konfiguration: ' . $e->getMessage());
}
}
}
```
Diese Konfiguration ermöglicht es, das Cryptography Module flexibel an verschiedene Umgebungen und Sicherheitsanforderungen anzupassen, während Best Practices für Sicherheit und Performance eingehalten werden.

View File

@@ -0,0 +1,703 @@
# Cryptography Module - Praktische Beispiele
Umfassende Sammlung praktischer Anwendungsfälle für das Cryptography Module.
## Authentifizierung und Session-Management
### User Password System
```php
final readonly class UserAuthenticationService
{
public function __construct(
private KeyDerivationFunction $kdf,
private SecureTokenGenerator $tokenGenerator,
private UserRepository $userRepository
) {}
public function registerUser(string $email, string $password): User
{
// Password hashen mit Argon2ID
$hashedPassword = $this->kdf->hashPassword($password, 'argon2id', [
'memory_cost' => 65536, // 64 MB
'time_cost' => 4, // 4 Iterationen
'threads' => 3 // 3 Threads
]);
$user = new User(
id: Ulid::generate(),
email: new Email($email),
passwordHash: $hashedPassword,
createdAt: new DateTimeImmutable()
);
return $this->userRepository->save($user);
}
public function authenticateUser(string $email, string $password): ?SessionToken
{
$user = $this->userRepository->findByEmail(new Email($email));
if (!$user || !$this->kdf->verify($password, $user->getPasswordHash())) {
// Timing-Angriffe verhindern durch konstante Ausführungszeit
$this->kdf->hashPassword('dummy-password', 'argon2id');
return null;
}
// Session Token generieren
$sessionToken = $this->tokenGenerator->generateSessionToken(48); // 384 Bit
// In Session Store speichern
$this->storeSessionToken($user, $sessionToken);
return $sessionToken;
}
private function storeSessionToken(User $user, SecureToken $token): void
{
$session = new UserSession(
tokenHash: hash('sha256', $token->getValue()),
userId: $user->getId(),
expiresAt: (new DateTimeImmutable())->add(new DateInterval('PT24H')),
metadata: $token->getMetadata()
);
// Session in Datenbank speichern (nur Hash, nie Klartext!)
$this->sessionRepository->save($session);
}
}
```
### API Key Management System
```php
final readonly class ApiKeyManagementService
{
public function __construct(
private SecureTokenGenerator $tokenGenerator,
private CryptographicUtilities $utils,
private ApiKeyRepository $repository
) {}
public function createApiKey(
User $user,
string $name,
array $scopes = [],
?DateTimeImmutable $expiresAt = null
): ApiKeyResult {
// API Key mit Prefix generieren
$apiKey = $this->tokenGenerator->generateApiKey(
prefix: 'ak',
length: 32
);
// API Key Entity erstellen (Hash speichern, nicht Klartext)
$apiKeyEntity = new ApiKey(
id: Ulid::generate(),
userId: $user->getId(),
name: $name,
keyHash: hash('sha256', $apiKey->getValue()),
fingerprint: $apiKey->getFingerprint(),
scopes: $scopes,
expiresAt: $expiresAt,
createdAt: new DateTimeImmutable()
);
$this->repository->save($apiKeyEntity);
return new ApiKeyResult(
apiKey: $apiKey->getValue(), // Nur einmal zurückgeben
keyId: $apiKeyEntity->getId(),
fingerprint: $apiKey->getShortFingerprint(),
expiresAt: $expiresAt
);
}
public function validateApiKey(string $apiKey): ?ApiKey
{
$keyHash = hash('sha256', $apiKey);
$storedKey = $this->repository->findByHash($keyHash);
if (!$storedKey) {
return null;
}
// Timing-sicherer Vergleich
if (!$this->utils->timingSafeEquals($keyHash, $storedKey->getKeyHash())) {
return null;
}
// Expiration prüfen
if ($storedKey->getExpiresAt() && $storedKey->getExpiresAt() < new DateTimeImmutable()) {
return null;
}
return $storedKey;
}
public function rotateApiKey(string $currentApiKey): ApiKeyResult
{
$oldKey = $this->validateApiKey($currentApiKey);
if (!$oldKey) {
throw new ApiKeyException('Invalid API key for rotation');
}
// Neuen Key mit gleichen Eigenschaften erstellen
$newApiKey = $this->createApiKey(
user: $oldKey->getUser(),
name: $oldKey->getName() . ' (rotated)',
scopes: $oldKey->getScopes(),
expiresAt: $oldKey->getExpiresAt()
);
// Alten Key deaktivieren (nicht löschen für Audit-Trail)
$oldKey->deactivate();
$this->repository->save($oldKey);
return $newApiKey;
}
}
```
## CSRF Protection
### CSRF Token System
```php
final readonly class CsrfProtectionService
{
public function __construct(
private SecureTokenGenerator $tokenGenerator,
private CacheInterface $cache
) {}
public function generateCsrfToken(string $sessionId): SecureToken
{
$csrfToken = $this->tokenGenerator->generateCsrfToken();
// Token mit Session verknüpfen
$cacheKey = "csrf_token_{$sessionId}";
$this->cache->set($cacheKey, $csrfToken->getValue(), 3600); // 1 Stunde
return $csrfToken;
}
public function validateCsrfToken(string $sessionId, string $providedToken): bool
{
$cacheKey = "csrf_token_{$sessionId}";
$storedToken = $this->cache->get($cacheKey);
if (!$storedToken) {
return false;
}
// Timing-sicherer Vergleich
$isValid = hash_equals($storedToken, $providedToken);
// Token nach Verwendung löschen (Single-Use)
if ($isValid) {
$this->cache->delete($cacheKey);
}
return $isValid;
}
}
// CSRF Middleware
final readonly class CsrfProtectionMiddleware
{
public function __construct(
private CsrfProtectionService $csrfService
) {}
public function handle(HttpRequest $request, RequestHandler $handler): HttpResponse
{
// Nur bei state-changing Operations prüfen
if (in_array($request->method, [Method::POST, Method::PUT, Method::DELETE, Method::PATCH])) {
$sessionId = $request->session->getId();
$csrfToken = $request->parsedBody->get('_token') ?? $request->headers->get('X-CSRF-Token');
if (!$csrfToken || !$this->csrfService->validateCsrfToken($sessionId, $csrfToken)) {
throw new CsrfTokenMismatchException('CSRF token mismatch');
}
}
return $handler->handle($request);
}
}
```
## Webhook Security
### Webhook Signature System
```php
final readonly class WebhookSecurityService
{
public function __construct(
private SecureTokenGenerator $tokenGenerator,
private CryptographicUtilities $utils
) {}
public function generateWebhookSecret(): SecureToken
{
return $this->tokenGenerator->generateWebhookToken();
}
public function signWebhookPayload(string $payload, SecureToken $secret): string
{
$signature = hash_hmac('sha256', $payload, $secret->getValue());
return 'sha256=' . $signature;
}
public function validateWebhookSignature(
string $payload,
string $signature,
SecureToken $secret
): bool {
$expectedSignature = $this->signWebhookPayload($payload, $secret);
// Timing-sicherer Vergleich
return $this->utils->timingSafeEquals($signature, $expectedSignature);
}
}
// Webhook Handler
final readonly class PaymentWebhookHandler
{
public function __construct(
private WebhookSecurityService $security,
private WebhookConfigRepository $configRepository
) {}
#[Route(path: '/webhooks/payment', method: Method::POST)]
public function handlePaymentWebhook(HttpRequest $request): JsonResult
{
$signature = $request->headers->get('X-Hub-Signature-256');
$payload = $request->rawBody();
if (!$signature) {
throw new WebhookException('Missing signature header');
}
// Webhook Secret für den Sender abrufen
$senderId = $request->headers->get('X-Sender-ID');
$config = $this->configRepository->findBySenderId($senderId);
if (!$config) {
throw new WebhookException('Unknown sender');
}
// Signatur validieren
if (!$this->security->validateWebhookSignature($payload, $signature, $config->getSecret())) {
throw new WebhookException('Invalid signature');
}
// Payload verarbeiten
$data = json_decode($payload, true);
$this->processPaymentEvent($data);
return new JsonResult(['status' => 'processed']);
}
}
```
## Two-Factor Authentication (2FA)
### OTP-basiertes 2FA System
```php
final readonly class TwoFactorAuthService
{
public function __construct(
private SecureTokenGenerator $tokenGenerator,
private CacheInterface $cache,
private NotificationService $notificationService
) {}
public function generateOtpCode(User $user): string
{
$otpToken = $this->tokenGenerator->generateOtpToken(6); // 6-stelliger Code
// OTP mit User-ID und Expiration speichern
$cacheKey = "otp_{$user->getId()}";
$otpData = [
'code' => $otpToken->getValue(),
'attempts' => 0,
'created_at' => time()
];
$this->cache->set($cacheKey, $otpData, 300); // 5 Minuten gültig
return $otpToken->getValue();
}
public function sendOtpCode(User $user): void
{
$otpCode = $this->generateOtpCode($user);
// SMS oder Email versenden
$this->notificationService->sendOtpCode(
recipient: $user->getPhoneNumber() ?? $user->getEmail(),
code: $otpCode,
expiresInMinutes: 5
);
}
public function verifyOtpCode(User $user, string $providedCode): bool
{
$cacheKey = "otp_{$user->getId()}";
$otpData = $this->cache->get($cacheKey);
if (!$otpData) {
return false; // Kein OTP vorhanden oder abgelaufen
}
// Rate-Limiting: Max 3 Versuche
if ($otpData['attempts'] >= 3) {
$this->cache->delete($cacheKey);
return false;
}
// Timing-sicherer Vergleich
$isValid = hash_equals($otpData['code'], $providedCode);
if ($isValid) {
// Erfolgreiche Verifikation - OTP löschen
$this->cache->delete($cacheKey);
} else {
// Fehlversuch - Counter erhöhen
$otpData['attempts']++;
$this->cache->set($cacheKey, $otpData, 300);
}
return $isValid;
}
}
```
## Digitale Signaturen für Dokumente
### Document Signing Service
```php
final readonly class DocumentSigningService
{
public function __construct(
private DigitalSignature $signature,
private DocumentRepository $documentRepository
) {}
public function generateSigningKeyPair(): KeyPair
{
// RSA 4096 Bit für langfristige Dokumentensignaturen
return $this->signature->generateRsaKeyPair(4096);
}
public function signDocument(
Document $document,
PrivateKey $privateKey,
User $signer
): SignedDocument {
$documentContent = $document->getContent();
$metadata = [
'document_id' => $document->getId(),
'signer_id' => $signer->getId(),
'signed_at' => (new DateTimeImmutable())->format('c'),
'algorithm' => 'RSA-SHA256'
];
// Dokument + Metadaten signieren
$dataToSign = $documentContent . json_encode($metadata);
$signatureResult = $this->signature->sign($dataToSign, $privateKey, 'sha256');
$signedDocument = new SignedDocument(
originalDocument: $document,
signature: $signatureResult,
signerPublicKey: $privateKey->getPublicKey(),
metadata: $metadata,
signedAt: new DateTimeImmutable()
);
return $this->documentRepository->saveSignedDocument($signedDocument);
}
public function verifyDocumentSignature(SignedDocument $signedDocument): bool
{
$document = $signedDocument->getOriginalDocument();
$metadata = $signedDocument->getMetadata();
$dataToVerify = $document->getContent() . json_encode($metadata);
return $this->signature->verify(
$dataToVerify,
$signedDocument->getSignature(),
$signedDocument->getSignerPublicKey()
);
}
public function createSignatureChain(array $documents, array $signers): SignatureChain
{
$chain = new SignatureChain();
foreach ($documents as $index => $document) {
$signer = $signers[$index];
$signedDocument = $this->signDocument($document, $signer->getPrivateKey(), $signer);
$chain->addSignedDocument($signedDocument);
}
// Chain-Signatur erstellen (Hash der gesamten Kette)
$chainHash = $chain->calculateChainHash();
$chainSignature = $this->signature->sign($chainHash, $signers[0]->getPrivateKey());
$chain->setChainSignature($chainSignature);
return $chain;
}
}
```
## Passwort-Reset System
### Secure Password Reset
```php
final readonly class PasswordResetService
{
public function __construct(
private SecureTokenGenerator $tokenGenerator,
private KeyDerivationFunction $kdf,
private CacheInterface $cache,
private EmailService $emailService
) {}
public function initiatePasswordReset(User $user): void
{
// Sicheren Reset-Token generieren
$resetToken = $this->tokenGenerator->generateVerificationToken('password_reset');
// Token hashen und speichern (nie Klartext speichern)
$tokenHash = hash('sha256', $resetToken->getValue());
$resetData = [
'user_id' => $user->getId(),
'token_hash' => $tokenHash,
'created_at' => time(),
'used' => false
];
// 2 Stunden gültig
$cacheKey = "password_reset_{$user->getId()}";
$this->cache->set($cacheKey, $resetData, 7200);
// Reset-Link per Email senden
$resetLink = "https://app.example.com/reset-password?token=" .
urlencode($resetToken->getValue());
$this->emailService->send(
to: $user->getEmail(),
subject: 'Password Reset Request',
template: 'password_reset',
data: ['reset_link' => $resetLink, 'expires_in' => '2 hours']
);
}
public function validateResetToken(string $token): ?User
{
$tokenHash = hash('sha256', $token);
// Token in allen aktiven Reset-Anfragen suchen
$cacheKeys = $this->cache->getKeysByPattern('password_reset_*');
foreach ($cacheKeys as $cacheKey) {
$resetData = $this->cache->get($cacheKey);
if ($resetData &&
!$resetData['used'] &&
hash_equals($resetData['token_hash'], $tokenHash)) {
return $this->userRepository->find($resetData['user_id']);
}
}
return null;
}
public function resetPassword(string $token, string $newPassword): bool
{
$user = $this->validateResetToken($token);
if (!$user) {
return false;
}
// Neues Password hashen
$hashedPassword = $this->kdf->hashPassword($newPassword, 'argon2id');
// User Password aktualisieren
$user->updatePassword($hashedPassword);
$this->userRepository->save($user);
// Reset-Token als verwendet markieren
$cacheKey = "password_reset_{$user->getId()}";
$resetData = $this->cache->get($cacheKey);
if ($resetData) {
$resetData['used'] = true;
$this->cache->set($cacheKey, $resetData, 7200);
}
return true;
}
}
```
## Daten-Verschlüsselung mit Schlüssel-Ableitung
### Encrypted Data Storage
```php
final readonly class EncryptedDataService
{
public function __construct(
private KeyDerivationFunction $kdf,
private CryptographicUtilities $utils,
private EncryptionService $encryption
) {}
public function encryptPersonalData(
array $personalData,
string $userPassword
): EncryptedPersonalData {
// Benutzer-spezifischen Schlüssel aus Password ableiten
$salt = $this->utils->generateNonce(32);
$derivedKey = $this->kdf->pbkdf2($userPassword, $salt, 100000, 32);
// Daten serialisieren und verschlüsseln
$serializedData = json_encode($personalData);
$encryptedData = $this->encryption->encrypt($serializedData, $derivedKey->getKey());
return new EncryptedPersonalData(
encryptedData: $encryptedData,
salt: $salt,
keyDerivation: [
'algorithm' => $derivedKey->getAlgorithm(),
'iterations' => $derivedKey->getIterations()
]
);
}
public function decryptPersonalData(
EncryptedPersonalData $encryptedData,
string $userPassword
): array {
// Gleichen Schlüssel aus Password ableiten
$derivedKey = $this->kdf->pbkdf2(
$userPassword,
$encryptedData->getSalt(),
$encryptedData->getKeyDerivation()['iterations'],
32
);
// Daten entschlüsseln
$decryptedData = $this->encryption->decrypt(
$encryptedData->getEncryptedData(),
$derivedKey->getKey()
);
return json_decode($decryptedData, true);
}
}
```
## Audit-Trail mit Kryptographischen Fingerprints
### Secure Audit Trail
```php
final readonly class AuditTrailService
{
public function __construct(
private CryptographicUtilities $utils,
private DigitalSignature $signature,
private AuditLogRepository $repository
) {}
public function logUserAction(
User $user,
string $action,
array $context = []
): AuditLogEntry {
$timestamp = new DateTimeImmutable();
$logData = [
'user_id' => $user->getId(),
'action' => $action,
'context' => $context,
'timestamp' => $timestamp->format('c'),
'ip_address' => $this->getClientIpHash(),
'user_agent_hash' => $this->getUserAgentHash()
];
// Kryptographischen Fingerprint erstellen
$dataString = json_encode($logData, JSON_SORT_KEYS);
$fingerprint = hash('sha256', $dataString);
// Chain-Hash (verhindert Manipulation der Historie)
$previousEntry = $this->repository->getLatestEntry();
$chainHash = $previousEntry
? hash('sha256', $previousEntry->getChainHash() . $fingerprint)
: $fingerprint;
$auditEntry = new AuditLogEntry(
id: Ulid::generate(),
fingerprint: $fingerprint,
chainHash: $chainHash,
logData: $logData,
createdAt: $timestamp
);
return $this->repository->save($auditEntry);
}
public function verifyAuditTrailIntegrity(): bool
{
$entries = $this->repository->getAllEntriesOrdered();
$previousChainHash = null;
foreach ($entries as $entry) {
// Fingerprint verifizieren
$dataString = json_encode($entry->getLogData(), JSON_SORT_KEYS);
$expectedFingerprint = hash('sha256', $dataString);
if (!hash_equals($entry->getFingerprint(), $expectedFingerprint)) {
return false; // Entry wurde manipuliert
}
// Chain-Hash verifizieren
$expectedChainHash = $previousChainHash
? hash('sha256', $previousChainHash . $entry->getFingerprint())
: $entry->getFingerprint();
if (!hash_equals($entry->getChainHash(), $expectedChainHash)) {
return false; // Chain wurde unterbrochen
}
$previousChainHash = $entry->getChainHash();
}
return true;
}
private function getClientIpHash(): string
{
$clientIp = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
return hash('sha256', $clientIp); // IP nicht im Klartext speichern
}
private function getUserAgentHash(): string
{
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
return hash('sha256', $userAgent);
}
}
```
Diese Beispiele zeigen die praktische Anwendung des Cryptography Modules in realen Szenarien und demonstrieren Best Practices für sichere Implementierungen.

View File

@@ -0,0 +1,365 @@
# Cryptography Module
Das Cryptography Module erweitert die bestehenden Verschlüsselungsfunktionen des Frameworks um moderne kryptographische Primitive und bietet sichere Implementierungen für Schlüsselableitung, digitale Signaturen, erweiterte Hash-Funktionen und sichere Token-Generierung.
## Überblick
Das Modul folgt den Framework-Prinzipien:
- **Immutable Value Objects** für sichere Datenrepräsentation
- **Service Pattern** für Business Logic
- **Dependency Injection** für RandomGenerator Integration
- **Security-First Design** mit timing-sicheren Operationen
## Core Services
### KeyDerivationFunction Service
Sichere Schlüsselableitung für Password-Hashing und Key-Stretching.
```php
use App\Framework\Cryptography\KeyDerivationFunction;
use App\Framework\Random\SecureRandomGenerator;
$kdf = new KeyDerivationFunction(new SecureRandomGenerator());
// PBKDF2 (Standard)
$derivedKey = $kdf->pbkdf2('password', $salt, 100000, 32);
// Argon2ID (Empfohlen)
$derivedKey = $kdf->argon2id('password', $salt, 65536, 4, 3, 32);
// Password mit automatischer Salt-Generierung hashen
$derivedKey = $kdf->hashPassword('password', 'argon2id');
// Password verifizieren
$isValid = $kdf->verify('password', $derivedKey);
```
**Unterstützte Algorithmen:**
- **PBKDF2** mit SHA-256, SHA-512, SHA-384, SHA-224
- **Argon2ID** (empfohlen für neue Implementierungen)
- **scrypt** für spezielle Anwendungsfälle
### DigitalSignature Service
Digitale Signaturen für Datenintegrität und Authentifizierung.
```php
use App\Framework\Cryptography\DigitalSignature;
$signature = new DigitalSignature(new SecureRandomGenerator());
// RSA Schlüsselpaar generieren
$keyPair = $signature->generateRsaKeyPair(2048);
// Daten signieren
$signatureResult = $signature->sign('data to sign', $keyPair->getPrivateKey());
// Signatur verifizieren
$isValid = $signature->verify('data to sign', $signatureResult, $keyPair->getPublicKey());
// ECDSA mit secp256r1
$ecKeyPair = $signature->generateEcdsaKeyPair('secp256r1');
$ecSignature = $signature->signWithEcdsa('data', $ecKeyPair->getPrivateKey());
```
### AdvancedHash Service
Erweiterte Hash-Funktionen über grundlegende Algorithmen hinaus.
```php
use App\Framework\Cryptography\AdvancedHash;
$hash = new AdvancedHash();
// SHA-3 Familie
$sha3 = $hash->sha3('data', 256);
$shake = $hash->shake('data', 32, 128); // Extendable-output function
// BLAKE2
$blake2b = $hash->blake2b('data', 32, 'optional-key');
$blake2s = $hash->blake2s('data', 16);
// Hash-Ketten für komplexe Szenarien
$chainResult = $hash->hashChain('data', ['sha3-256', 'blake2b']);
```
### SecureTokenGenerator Service
Kryptographisch sichere Token-Generierung für APIs, Sessions und mehr.
```php
use App\Framework\Cryptography\SecureTokenGenerator;
$generator = new SecureTokenGenerator(new SecureRandomGenerator());
// API Keys mit Prefix
$apiKey = $generator->generateApiKey('myapp'); // myapp_...
// Session Tokens
$sessionToken = $generator->generateSessionToken();
// CSRF Tokens
$csrfToken = $generator->generateCsrfToken();
// OTP Tokens
$otp = $generator->generateOtpToken(6); // 6-stelliger numerischer Code
// Custom Tokens mit eigenem Alphabet
$customToken = $generator->generateCustom('0123456789ABCDEF', 16);
// Batch-Generierung
$tokens = $generator->generateBatch('session', 10, 32);
```
**Token-Formate:**
- `FORMAT_BASE64_URL` (Standard, URL-sicher)
- `FORMAT_BASE64` (Standard Base64)
- `FORMAT_HEX` (Hexadezimal)
- `FORMAT_BASE32` (Base32)
- `FORMAT_ALPHANUMERIC` (Buchstaben + Zahlen)
### CryptographicUtilities Service
Sammlung kryptographischer Utility-Funktionen.
```php
use App\Framework\Cryptography\CryptographicUtilities;
$utils = new CryptographicUtilities(new SecureRandomGenerator());
// Timing-sichere String-Vergleiche
$isEqual = $utils->timingSafeEquals($string1, $string2);
// Entropie-Validierung
$isHighEntropy = $utils->validateEntropy($data, 7.0);
$entropy = $utils->calculateShannonEntropy($data);
// Sichere UUIDs
$uuid4 = $utils->generateUuid4(); // Zufällige UUID
$uuid5 = $utils->generateUuid5($namespace, 'name'); // Deterministische UUID
// Key-Stretching
$stretched = $utils->stretchKey('password', 'salt', 10000, 32);
// Speicher sicher löschen
$utils->secureWipe($sensitiveData);
```
## Value Objects
### DerivedKey
Unveränderliche Repräsentation abgeleiteter Schlüssel.
```php
// Eigenschaften abrufen
$algorithm = $derivedKey->getAlgorithm(); // 'pbkdf2-sha256'
$keyLength = $derivedKey->getKeyLength(); // 32
$iterations = $derivedKey->getIterations(); // 100000
// Verschiedene Formate
$hex = $derivedKey->toHex();
$base64 = $derivedKey->toBase64();
// Serialisierung
$array = $derivedKey->toArray();
$restored = DerivedKey::fromArray($array);
// Gleichheit prüfen (timing-sicher)
$isEqual = $derivedKey->equals($otherKey);
```
### SecureToken
Token mit Metadaten und sicherheitsfokussierten Funktionen.
```php
// Token-Eigenschaften
$type = $token->getType(); // 'api_key'
$format = $token->getFormat(); // 'base64_url'
$hasPrefix = $token->hasPrefix(); // true/false
// Sichere Operationen
$isEqual = $token->equals($otherToken); // timing-sicher
$isValid = $token->verify($candidateToken); // timing-sicher
// Sicherheit und Logging
$masked = $token->getMaskedValue(); // myap****_Ab...fG
$fingerprint = $token->getFingerprint(); // SHA-256 Hash
$safeSummary = $token->getSafeSummary(); // Ohne Token-Wert
```
### KeyPair, PrivateKey, PublicKey
Sichere Verwaltung asymmetrischer Schlüssel.
```php
$keyPair = $signature->generateRsaKeyPair(2048);
$privateKey = $keyPair->getPrivateKey();
$publicKey = $keyPair->getPublicKey();
// Schlüssel-Export
$privatePem = $privateKey->toPem();
$publicPem = $publicKey->toPem();
// Schlüssel-Import
$privateKey = PrivateKey::fromPem($privatePem);
$publicKey = PublicKey::fromPem($publicPem);
```
## Sicherheitsfeatures
### Timing-Attack Schutz
Alle kritischen Vergleichsoperationen verwenden timing-sichere Implementierungen:
```php
// Timing-sichere String-Vergleiche
$utils->timingSafeEquals($secret1, $secret2);
// Timing-sichere Token-Verifikation
$token->verify($candidateToken);
// Timing-sichere Array-Suche
$utils->constantTimeArraySearch($haystack, $needle);
```
### Entropie-Validierung
Automatische Validierung der Schlüsselstärke:
```php
// Shannon-Entropie berechnen
$entropy = $utils->calculateShannonEntropy($data);
// Mindest-Entropie validieren
$isStrong = $utils->validateEntropy($data, 7.0);
// Schlüsselstärke prüfen
$isValidKey = $utils->validateKeyStrength($key, 128); // Minimum 128 Bits
```
### Sichere Speicher-Verwaltung
```php
// Sensitive Daten sicher löschen
$utils->secureWipe($password); // Überschreibt Speicher
// Automatische Metadaten-Bereinigung in Token-Logs
$safeSummary = $token->getSafeSummary(); // Ohne sensitive Werte
```
## Best Practices
### Empfohlene Parameter
```php
// PBKDF2 (Minimum)
$kdf->pbkdf2($password, $salt, 100000, 32, 'sha256');
// Argon2ID (Empfohlen)
$kdf->argon2id($password, $salt, 65536, 4, 3, 32);
// RSA Schlüssel (Minimum 2048 Bit)
$signature->generateRsaKeyPair(2048);
// Token-Längen
$generator->generateApiKey('prefix', 32); // 256 Bit
$generator->generateSessionToken(48); // 384 Bit für langlebige Sessions
```
### Sichere Implementierung
```php
// ✅ Gute Praxis
final readonly class AuthService
{
public function __construct(
private readonly KeyDerivationFunction $kdf,
private readonly SecureTokenGenerator $tokenGenerator
) {}
public function hashPassword(string $password): DerivedKey
{
return $this->kdf->hashPassword($password, 'argon2id');
}
public function generateApiKey(string $prefix): SecureToken
{
return $this->tokenGenerator->generateApiKey($prefix, 32);
}
}
// ❌ Schlechte Praxis - Nicht verwenden
// $hashedPassword = md5($password); // Unsicher
// $token = bin2hex(random_bytes(16)); // Zu kurz, keine Metadaten
```
## Integration mit Framework
### Dependency Injection
```php
final readonly class CryptographyServiceInitializer implements Initializer
{
public function initialize(Container $container): void
{
$randomGenerator = $container->get(RandomGenerator::class);
$container->singleton(KeyDerivationFunction::class,
new KeyDerivationFunction($randomGenerator));
$container->singleton(SecureTokenGenerator::class,
new SecureTokenGenerator($randomGenerator));
$container->singleton(CryptographicUtilities::class,
new CryptographicUtilities($randomGenerator));
}
}
```
### Controller Integration
```php
final readonly class ApiKeyController
{
#[Route(path: '/admin/api-keys', method: Method::POST)]
#[Auth(strategy: 'ip', allowedIps: ['127.0.0.1'])]
public function createApiKey(
CreateApiKeyRequest $request,
SecureTokenGenerator $tokenGenerator
): JsonResult {
$apiKey = $tokenGenerator->generateApiKey(
prefix: $request->prefix,
length: 32
);
// Token sicher in Datenbank speichern
// Nur Hash oder verschlüsselte Version speichern, nie Klartext
return new JsonResult([
'api_key' => $apiKey->getValue(),
'fingerprint' => $apiKey->getShortFingerprint(),
'created_at' => $apiKey->getCreatedAt()->format('c')
]);
}
}
```
## Performance
Das Cryptography Module ist für Production-Einsatz optimiert:
- **Cached Reflection Provider** für Dependency Injection
- **Batch-Operationen** für Token-Generierung
- **Effiziente Algorithmen** mit modernen kryptographischen Standards
- **Minimale Speicher-Allokation** durch readonly Value Objects
## Weiterführende Dokumentation
- [Konfiguration](configuration.md) - Setup und Anpassung
- [Beispiele](examples.md) - Praktische Anwendungsfälle
- [Migration](migration.md) - Upgrade von älteren Krypto-Implementierungen
- [Sicherheit](security.md) - Detaillierte Sicherheitsrichtlinien

View File

@@ -0,0 +1,728 @@
# Cryptography Module - Sicherheitsrichtlinien
Detaillierte Sicherheitsrichtlinien und Best Practices für das Cryptography Module.
## Grundlegende Sicherheitsprinzipien
### Defense in Depth
Das Cryptography Module implementiert mehrschichtigen Schutz:
1. **Algorithmus-Ebene**: Moderne, peer-reviewed Algorithmen (Argon2ID, SHA-3, ECDSA)
2. **Implementation-Ebene**: Timing-sichere Operationen und sichere Speicherverwaltung
3. **Application-Ebene**: Sichere Integration und Konfiguration
4. **System-Ebene**: Sicherer Random Number Generator und Entropie-Validierung
### Zero-Trust Architektur
```php
// ✅ Vertraue niemals Eingaben - validiere alles
final readonly class SecureTokenValidator
{
public function validateApiKey(string $providedKey): ?ApiKeyContext
{
// Eingabe-Validierung
if (empty($providedKey) || strlen($providedKey) < 32) {
return null;
}
// Format-Validierung
if (!$this->tokenGenerator->isValidFormat($providedKey, 'base64_url')) {
return null;
}
// Timing-sicherer Vergleich mit gespeichertem Hash
$keyHash = hash('sha256', $providedKey);
$storedKey = $this->repository->findByHash($keyHash);
if (!$storedKey || !hash_equals($keyHash, $storedKey->getHash())) {
return null;
}
// Zusätzliche Sicherheitsprüfungen
return $this->performSecurityChecks($storedKey);
}
}
```
## Kryptographische Best Practices
### Sichere Schlüssel-Ableitung
```php
// ✅ Empfohlene Parameter für verschiedene Szenarien
final readonly class SecureKeyDerivation
{
private const SECURITY_LEVELS = [
// Hohe Sicherheit für sensitive Daten
'high_security' => [
'algorithm' => 'argon2id',
'memory_cost' => 131072, // 128 MB
'time_cost' => 6, // 6 Iterationen
'threads' => 4, // 4 Threads
'key_length' => 32 // 256 Bit
],
// Standard für normale Anwendungen
'standard' => [
'algorithm' => 'argon2id',
'memory_cost' => 65536, // 64 MB
'time_cost' => 4, // 4 Iterationen
'threads' => 3, // 3 Threads
'key_length' => 32 // 256 Bit
],
// Kompatibilität mit älteren Systemen
'legacy_compatible' => [
'algorithm' => 'pbkdf2-sha256',
'iterations' => 200000, // 200k Iterationen
'key_length' => 32 // 256 Bit
]
];
public function hashPassword(string $password, string $level = 'standard'): DerivedKey
{
$params = self::SECURITY_LEVELS[$level] ?? self::SECURITY_LEVELS['standard'];
return match ($params['algorithm']) {
'argon2id' => $this->kdf->argon2id(
$password,
$this->kdf->generateSalt(32),
$params['memory_cost'],
$params['time_cost'],
$params['threads'],
$params['key_length']
),
'pbkdf2-sha256' => $this->kdf->pbkdf2(
$password,
$this->kdf->generateSalt(32),
$params['iterations'],
$params['key_length']
)
};
}
}
```
### Sichere Token-Generierung
```php
// ✅ Token-Sicherheit nach Verwendungszweck
final readonly class SecurityAwareTokenGenerator
{
private const TOKEN_SECURITY_REQUIREMENTS = [
'api_key' => [
'length' => 32, // 256 Bit
'format' => 'base64_url',
'entropy_min' => 7.0,
'expires' => false, // Langlebig
'single_use' => false
],
'session' => [
'length' => 48, // 384 Bit (höhere Sicherheit)
'format' => 'base64_url',
'entropy_min' => 7.5,
'expires' => true,
'single_use' => false
],
'csrf' => [
'length' => 32, // 256 Bit
'format' => 'base64_url',
'entropy_min' => 7.0,
'expires' => true, // Kurze Lebensdauer
'single_use' => true // Einmalverwendung
],
'password_reset' => [
'length' => 64, // 512 Bit (maximale Sicherheit)
'format' => 'base64_url',
'entropy_min' => 7.8,
'expires' => true,
'single_use' => true,
'max_lifetime' => 3600 * 2 // 2 Stunden
]
];
public function generateSecureToken(string $type): SecureToken
{
$requirements = self::TOKEN_SECURITY_REQUIREMENTS[$type] ??
throw new InvalidArgumentException("Unknown token type: {$type}");
do {
$token = $this->tokenGenerator->generate(
$type,
$requirements['length'],
$requirements['format']
);
// Entropie validieren
$entropy = $this->utils->calculateShannonEntropy($token->getRawBytes());
} while ($entropy < $requirements['entropy_min']);
return $token;
}
}
```
## Timing-Attack Prevention
### Konstante Ausführungszeit
```php
// ✅ Timing-sichere Implementierung
final readonly class TimingSafeOperations
{
public function authenticateUser(string $email, string $password): ?User
{
$user = $this->userRepository->findByEmail(new Email($email));
// WICHTIG: Immer Hashing durchführen, auch bei ungültigem User
$providedHash = $user
? $user->getPasswordHash()
: $this->createDummyHash(); // Konstante Ausführungszeit
$isValid = $this->kdf->verify($password, $providedHash);
// Zusätzliche konstante Verzögerung
$this->enforceConstantTime();
return $isValid && $user ? $user : null;
}
private function createDummyHash(): DerivedKey
{
// Dummy-Hash mit gleichen Parametern wie echte Hashes
static $dummyHash = null;
if ($dummyHash === null) {
$dummyHash = $this->kdf->hashPassword('dummy-password', 'argon2id');
}
return $dummyHash;
}
private function enforceConstantTime(): void
{
// Minimale zusätzliche Verzögerung (1-5ms)
usleep(random_int(1000, 5000));
}
}
```
### Side-Channel Resistance
```php
// ✅ Schutz vor Side-Channel-Angriffen
final readonly class SideChannelResistantComparison
{
public function compareSecrets(string $secret1, string $secret2): bool
{
// PHP's hash_equals ist timing-sicher implementiert
return hash_equals($secret1, $secret2);
}
public function compareArrays(array $array1, array $array2): bool
{
// Arrays zu Strings serialisieren für timing-sicheren Vergleich
$string1 = json_encode($array1, JSON_SORT_KEYS);
$string2 = json_encode($array2, JSON_SORT_KEYS);
return hash_equals($string1, $string2);
}
public function searchInArray(array $haystack, mixed $needle): bool
{
$found = false;
// Alle Elemente durchgehen (konstante Zeit)
foreach ($haystack as $value) {
$isMatch = is_string($value) && is_string($needle)
? hash_equals($value, $needle)
: ($value === $needle);
$found = $found || $isMatch; // Kein frühes Beenden
}
return $found;
}
}
```
## Sichere Speicherverwaltung
### Sensitive Data Handling
```php
// ✅ Sicherer Umgang mit sensitiven Daten
final readonly class SecureMemoryManagement
{
public function processSecretData(string $secretData): ProcessedResult
{
try {
// Daten verarbeiten
$result = $this->doSecretProcessing($secretData);
// Sensitive Daten aus Speicher löschen
$this->utils->secureWipe($secretData);
return $result;
} catch (Throwable $e) {
// Auch bei Fehlern Speicher löschen
$this->utils->secureWipe($secretData);
throw $e;
}
}
public function createSecureTemporaryFile(string $data): SecureTemporaryFile
{
// Temporary file mit restriktiven Berechtigungen erstellen
$tempFile = tempnam(sys_get_temp_dir(), 'secure_');
chmod($tempFile, 0600); // Nur Owner kann lesen/schreiben
// Daten schreiben und sofort aus Speicher löschen
file_put_contents($tempFile, $data);
$this->utils->secureWipe($data);
// Auto-cleanup mit Destruktor
return new SecureTemporaryFile($tempFile);
}
}
// Auto-cleanup für temporäre Dateien
final class SecureTemporaryFile
{
public function __construct(private string $filePath) {}
public function __destruct()
{
if (file_exists($this->filePath)) {
// Datei mehrfach überschreiben vor dem Löschen
$fileSize = filesize($this->filePath);
for ($i = 0; $i < 3; $i++) {
file_put_contents($this->filePath, random_bytes($fileSize));
}
unlink($this->filePath);
}
}
public function getPath(): string
{
return $this->filePath;
}
}
```
## Input Validation und Sanitization
### Comprehensive Input Validation
```php
// ✅ Umfassende Eingabe-Validierung
final readonly class CryptographicInputValidator
{
public function validatePasswordStrength(string $password): ValidationResult
{
$errors = [];
// Länge prüfen
if (strlen($password) < 8) {
$errors[] = 'Password must be at least 8 characters long';
}
// Komplexität prüfen
if (!preg_match('/[A-Z]/', $password)) {
$errors[] = 'Password must contain uppercase letters';
}
if (!preg_match('/[a-z]/', $password)) {
$errors[] = 'Password must contain lowercase letters';
}
if (!preg_match('/[0-9]/', $password)) {
$errors[] = 'Password must contain numbers';
}
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
$errors[] = 'Password must contain special characters';
}
// Entropie prüfen
$entropy = $this->utils->calculateShannonEntropy($password);
if ($entropy < 3.5) {
$errors[] = 'Password has insufficient entropy';
}
// Gegen Common-Passwords prüfen
if ($this->isCommonPassword($password)) {
$errors[] = 'Password is too common';
}
return new ValidationResult(empty($errors), $errors);
}
public function validateApiKeyFormat(string $apiKey): bool
{
// Länge prüfen (32-64 Zeichen)
if (strlen($apiKey) < 32 || strlen($apiKey) > 64) {
return false;
}
// Format prüfen (Base64URL)
if (!preg_match('/^[A-Za-z0-9_-]+$/', $apiKey)) {
return false;
}
// Entropie validieren
$entropy = $this->utils->calculateShannonEntropy($apiKey);
return $entropy >= 6.0;
}
public function sanitizeUserInput(string $input): string
{
// Whitespace normalisieren
$sanitized = trim($input);
// Null-Bytes entfernen (verhindern Directory Traversal)
$sanitized = str_replace("\0", '', $sanitized);
// Control Characters entfernen
$sanitized = preg_replace('/[\x00-\x1F\x7F]/', '', $sanitized);
return $sanitized;
}
private function isCommonPassword(string $password): bool
{
$commonPasswords = [
'password', '123456', 'password123', 'admin', 'qwerty',
'letmein', 'welcome', 'monkey', '1234567890', 'password1'
];
$lowercase = strtolower($password);
foreach ($commonPasswords as $common) {
if ($lowercase === $common ||
levenshtein($lowercase, $common) <= 2) {
return true;
}
}
return false;
}
}
```
## Rate Limiting und Abuse Prevention
### Cryptographic Rate Limiting
```php
// ✅ Rate-Limiting für kryptographische Operationen
final readonly class CryptographicRateLimiter
{
private const LIMITS = [
'password_attempts' => ['count' => 5, 'window' => 900], // 5 in 15 Min
'token_generation' => ['count' => 100, 'window' => 3600], // 100 in 1 Stunde
'password_reset' => ['count' => 3, 'window' => 3600], // 3 in 1 Stunde
'otp_generation' => ['count' => 5, 'window' => 300], // 5 in 5 Min
];
public function __construct(
private CacheInterface $cache,
private SecurityEventLogger $securityLogger
) {}
public function checkLimit(string $operation, string $identifier): bool
{
$limit = self::LIMITS[$operation] ?? null;
if (!$limit) {
return true; // Kein Limit definiert
}
$cacheKey = "ratelimit_{$operation}_{$identifier}";
$attempts = $this->cache->get($cacheKey, []);
$now = time();
// Abgelaufene Versuche entfernen
$attempts = array_filter($attempts, fn($timestamp) =>
$now - $timestamp < $limit['window']);
if (count($attempts) >= $limit['count']) {
// Rate-Limit erreicht
$this->securityLogger->logRateLimitExceeded($operation, $identifier);
return false;
}
// Neuen Versuch hinzufügen
$attempts[] = $now;
$this->cache->set($cacheKey, $attempts, $limit['window']);
return true;
}
public function recordFailedAttempt(string $operation, string $identifier): void
{
$cacheKey = "failed_{$operation}_{$identifier}";
$failures = $this->cache->get($cacheKey, 0);
$failures++;
$this->cache->set($cacheKey, $failures, 3600); // 1 Stunde
// Bei vielen Fehlversuchen Sicherheitsteam benachrichtigen
if ($failures >= 10) {
$this->securityLogger->logSuspiciousActivity(
$operation,
$identifier,
"Multiple failed attempts: {$failures}"
);
}
}
}
```
## Error Handling und Information Leakage Prevention
### Secure Error Handling
```php
// ✅ Sichere Fehlerbehandlung ohne Information Leakage
final readonly class SecureErrorHandler
{
public function handleCryptographicError(Throwable $error): ErrorResponse
{
// Detaillierte Logs für interne Analyse
$this->logger->error('Cryptographic error occurred', [
'error_type' => get_class($error),
'message' => $error->getMessage(),
'trace' => $error->getTraceAsString(),
'context' => $this->getSecureContext()
]);
// Generische Antwort für Client (keine Details)
$publicMessage = match (true) {
$error instanceof InvalidPasswordException => 'Authentication failed',
$error instanceof TokenExpiredException => 'Token has expired',
$error instanceof TokenInvalidException => 'Invalid token',
$error instanceof CryptographicException => 'Security operation failed',
default => 'Internal error occurred'
};
return new ErrorResponse(
message: $publicMessage,
code: $this->getPublicErrorCode($error),
timestamp: new DateTimeImmutable()
);
}
private function getSecureContext(): array
{
return [
'request_id' => $this->generateRequestId(),
'user_agent_hash' => hash('sha256', $_SERVER['HTTP_USER_AGENT'] ?? ''),
'ip_hash' => hash('sha256', $_SERVER['REMOTE_ADDR'] ?? ''),
'timestamp' => time()
];
}
private function generateRequestId(): string
{
return bin2hex(random_bytes(16));
}
}
```
## Monitoring und Alerting
### Security Monitoring
```php
// ✅ Sicherheits-Monitoring für kryptographische Operationen
final readonly class CryptographicSecurityMonitor
{
public function __construct(
private MetricsCollector $metrics,
private AlertManager $alerts,
private SecurityEventLogger $securityLogger
) {}
public function monitorOperation(string $operation, callable $callback): mixed
{
$startTime = microtime(true);
$operationId = bin2hex(random_bytes(8));
try {
$result = $callback();
// Erfolgreiche Operation protokollieren
$duration = microtime(true) - $startTime;
$this->metrics->increment("crypto.{$operation}.success");
$this->metrics->timing("crypto.{$operation}.duration", $duration);
// Ungewöhnlich lange Operationen melden
if ($duration > $this->getExpectedDuration($operation) * 2) {
$this->alerts->sendAlert(
level: 'warning',
message: "Slow cryptographic operation: {$operation}",
context: ['duration' => $duration, 'operation_id' => $operationId]
);
}
return $result;
} catch (Throwable $e) {
// Fehlgeschlagene Operation protokollieren
$this->metrics->increment("crypto.{$operation}.failure");
$this->securityLogger->logCryptographicFailure($operation, $e, $operationId);
// Bei kritischen Fehlern sofort alarmieren
if ($this->isCriticalError($e)) {
$this->alerts->sendAlert(
level: 'critical',
message: "Critical cryptographic failure: {$operation}",
context: [
'error' => $e->getMessage(),
'operation_id' => $operationId
]
);
}
throw $e;
}
}
public function detectAnomalousPatterns(): void
{
// Erhöhte Fehlerrate erkennen
$errorRate = $this->metrics->getRate('crypto.*.failure', '5m');
if ($errorRate > 0.1) { // >10% Fehlerrate
$this->alerts->sendAlert(
level: 'warning',
message: 'High cryptographic error rate detected',
context: ['error_rate' => $errorRate]
);
}
// Ungewöhnliche Aktivitätsmuster
$operations = $this->metrics->getCount('crypto.*.success', '1h');
$baseline = $this->getHistoricalBaseline();
if ($operations > $baseline * 5) {
$this->alerts->sendAlert(
level: 'warning',
message: 'Unusual spike in cryptographic operations',
context: ['operations' => $operations, 'baseline' => $baseline]
);
}
}
private function getExpectedDuration(string $operation): float
{
return match ($operation) {
'token_generation' => 0.001, // 1ms
'password_hash' => 0.5, // 500ms (Argon2ID)
'password_verify' => 0.5, // 500ms
'signature_create' => 0.01, // 10ms
'signature_verify' => 0.005, // 5ms
default => 0.1 // 100ms
};
}
}
```
## Compliance und Audit
### Compliance Framework
```php
// ✅ Compliance-Unterstützung für Regulierungen
final readonly class CryptographicComplianceValidator
{
private const COMPLIANCE_REQUIREMENTS = [
'GDPR' => [
'encryption_required' => true,
'key_length_min' => 256,
'algorithm_approved' => ['AES-256-GCM', 'ChaCha20-Poly1305'],
'key_rotation_days' => 365,
'audit_trail_required' => true
],
'PCI_DSS' => [
'encryption_required' => true,
'key_length_min' => 256,
'algorithm_approved' => ['AES-256', 'RSA-2048'],
'key_rotation_days' => 365,
'secure_key_storage' => true
],
'FIPS_140_2' => [
'approved_algorithms_only' => true,
'algorithm_approved' => ['AES', 'SHA-256', 'RSA', 'ECDSA'],
'random_number_generator' => 'FIPS_approved',
'key_length_min' => 256
]
];
public function validateCompliance(string $standard): ComplianceReport
{
$requirements = self::COMPLIANCE_REQUIREMENTS[$standard] ??
throw new InvalidArgumentException("Unknown standard: {$standard}");
$violations = [];
$report = new ComplianceReport($standard);
// Algorithmus-Compliance prüfen
if (isset($requirements['algorithm_approved'])) {
$usedAlgorithms = $this->getUsedAlgorithms();
$approvedAlgorithms = $requirements['algorithm_approved'];
foreach ($usedAlgorithms as $algorithm) {
if (!in_array($algorithm, $approvedAlgorithms)) {
$violations[] = "Non-approved algorithm in use: {$algorithm}";
}
}
}
// Schlüssel-Längen prüfen
if (isset($requirements['key_length_min'])) {
$keyLengths = $this->getKeyLengths();
$minLength = $requirements['key_length_min'];
foreach ($keyLengths as $context => $length) {
if ($length < $minLength) {
$violations[] = "Key length too short in {$context}: {$length} < {$minLength}";
}
}
}
// Schlüssel-Rotation prüfen
if (isset($requirements['key_rotation_days'])) {
$rotationViolations = $this->checkKeyRotation($requirements['key_rotation_days']);
$violations = array_merge($violations, $rotationViolations);
}
$report->setViolations($violations);
$report->setCompliant(empty($violations));
return $report;
}
public function generateAuditReport(): AuditReport
{
return new AuditReport([
'cryptographic_operations' => $this->getCryptoOperationStats(),
'key_management' => $this->getKeyManagementStats(),
'algorithm_usage' => $this->getAlgorithmUsageStats(),
'security_events' => $this->getSecurityEventSummary(),
'compliance_status' => $this->getComplianceStatus()
]);
}
}
```
Diese Sicherheitsrichtlinien stellen sicher, dass das Cryptography Module höchste Sicherheitsstandards erfüllt und gegen moderne Angriffsvektoren geschützt ist.

View File

@@ -0,0 +1,417 @@
# 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)

View File

@@ -0,0 +1,363 @@
# Security Features
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der Sicherheitsfeatures korrekt dar.
## Übersicht
Das Framework bietet umfassende Sicherheitsfunktionen, die Ihre Anwendung vor gängigen Bedrohungen schützen. Diese Funktionen sind tief in die Architektur integriert und folgen modernen Best Practices für Webanwendungssicherheit.
## Hauptkomponenten
### CSRF-Schutz
Der Cross-Site Request Forgery (CSRF) Schutz verhindert, dass Angreifer unerwünschte Aktionen im Namen authentifizierter Benutzer ausführen:
```php
// Token generieren
$csrfToken = $csrfTokenGenerator->generate();
// Token in Formular einbinden
$form = '<input type="hidden" name="csrf_token" value="' . $csrfToken->toString() . '">';
// Token validieren
if (!$csrfToken->equalsString($request->getPostParam('csrf_token'))) {
throw new SecurityException('Ungültiges CSRF-Token');
}
```
### Security Headers
Die `SecurityHeaderMiddleware` fügt automatisch wichtige Sicherheits-Header zu allen HTTP-Antworten hinzu:
- **Strict-Transport-Security (HSTS)**: Erzwingt HTTPS-Verbindungen
- **X-Frame-Options**: Verhindert Clickjacking-Angriffe
- **X-Content-Type-Options**: Verhindert MIME-Sniffing
- **Content-Security-Policy (CSP)**: Schützt vor XSS und anderen Injection-Angriffen
- **Referrer-Policy**: Kontrolliert die Weitergabe von Referrer-Informationen
- **Permissions-Policy**: Beschränkt Browser-Features
- **Cross-Origin-Policies**: Steuert ressourcenübergreifende Interaktionen
```php
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(SecurityHeaderMiddleware::class);
```
### Request Signing
Das Request-Signing-System ermöglicht die kryptografische Verifizierung von API-Anfragen:
```php
// Ausgehende Anfrage signieren
$signedRequest = $requestSigningService->signOutgoingRequest($request);
// Eingehende Anfrage verifizieren
$result = $requestSigningService->verifyIncomingRequest($request);
if (!$result->isSuccess()) {
throw new SecurityException('Ungültige Anfrage-Signatur: ' . $result->errorMessage);
}
```
### Session-Sicherheit
Der `SecurityManager` schützt Benutzersitzungen vor verschiedenen Angriffen:
- **Session-Fixierung**: Automatische Regenerierung von Session-IDs
- **Session-Hijacking**: Überprüfung von IP-Adressen und User-Agents
- **Session-Timeout**: Automatisches Beenden inaktiver Sitzungen
- **Concurrent-Login-Erkennung**: Erkennung gleichzeitiger Anmeldungen
```php
// In einem Controller oder Middleware
$securityManager->validateSession($request, $session);
```
### Sicherheits-Ereignisbehandlung
Das Framework bietet ein umfassendes System zur Erkennung und Protokollierung von Sicherheitsereignissen:
```php
// Sicherheitsereignis protokollieren
$securityEventLogger->logEvent(
SecurityEventType::AUTHENTICATION_FAILURE,
'Fehlgeschlagene Anmeldung',
['username' => $username, 'ip' => $request->getClientIp()]
);
// Auf Sicherheitsereignisse reagieren
$securityAlertManager->handleEvent($event);
```
## Konfiguration
### CSRF-Schutz konfigurieren
```php
// config/security.php
return [
'csrf' => [
'enabled' => true,
'token_lifetime' => 3600, // Sekunden
'exclude_routes' => [
'/api/webhook',
],
],
];
```
### Security Headers konfigurieren
```php
// config/security.php
return [
'headers' => [
'strict_mode' => true, // Produktionsmodus mit strengeren Einstellungen
'content_security_policy' => "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';",
'hsts' => [
'enabled' => true,
'max_age' => 31536000, // 1 Jahr
'include_subdomains' => true,
'preload' => true,
],
],
];
```
### Request Signing konfigurieren
```php
// config/security.php
return [
'request_signing' => [
'enabled' => true,
'default_algorithm' => 'hmac-sha256',
'default_headers' => ['(request-target)', 'host', 'date', 'content-type'],
'default_expiry' => 300, // Sekunden
'required_routes' => [
'/api/admin/*',
'/api/payments/*',
],
],
];
```
## Verwendung
### CSRF-Schutz in Formularen
```php
// In einem Controller
public function showForm(): Response
{
$csrfToken = $this->csrfTokenGenerator->generate();
$this->session->set('csrf_token', $csrfToken->toString());
return $this->render('form.twig', [
'csrf_token' => $csrfToken->toString(),
]);
}
public function handleForm(Request $request): Response
{
$storedToken = CsrfToken::fromString($this->session->get('csrf_token'));
$submittedToken = $request->getPostParam('csrf_token');
if (!$storedToken->equalsString($submittedToken)) {
throw new SecurityException('Ungültiges CSRF-Token');
}
// Formular verarbeiten
}
```
### Content Security Policy anpassen
```php
// In einem Controller oder Middleware
public function __invoke(MiddlewareContext $context, Next $next): MiddlewareContext
{
$resultContext = $next($context);
if ($resultContext->hasResponse()) {
$response = $resultContext->response;
$headers = $response->headers;
// CSP für eine bestimmte Route anpassen
$updatedHeaders = $headers->with(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' https://trusted-cdn.com;"
);
$updatedResponse = $this->manipulator->withHeaders($response, $updatedHeaders);
return $resultContext->withResponse($updatedResponse);
}
return $resultContext;
}
```
### API-Anfragen signieren und verifizieren
```php
// Client-Seite: Anfrage signieren
$request = new HttpRequest('POST', '/api/data');
$signedRequest = $this->requestSigningService->signOutgoingRequest($request);
$response = $this->httpClient->send($signedRequest);
// Server-Seite: Anfrage verifizieren (in Middleware)
public function __invoke(MiddlewareContext $context, Next $next): MiddlewareContext
{
$request = $context->request;
// Prüfen, ob die Route signierte Anfragen erfordert
if ($this->requiresSignature($request->path)) {
$result = $this->requestSigningService->verifyIncomingRequest($request);
if (!$result->isSuccess()) {
return $context->withResponse(
new Response(401, [], ['error' => 'Ungültige Signatur: ' . $result->errorMessage])
);
}
}
return $next($context);
}
```
### Schlüsselverwaltung für Request Signing
```php
// HMAC-Schlüssel generieren
$key = $requestSigningService->generateHmacKey(
'api-key-1',
SigningAlgorithm::HMAC_SHA256,
new \DateTimeImmutable('+30 days')
);
// RSA-Schlüssel erstellen
$privateKey = file_get_contents('private_key.pem');
$key = $requestSigningService->createRsaKey(
'api-key-2',
$privateKey,
new \DateTimeImmutable('+1 year')
);
// Schlüssel rotieren
$newKey = SigningKey::generateHmac('api-key-1-new', SigningAlgorithm::HMAC_SHA256);
$requestSigningService->rotateSigningKey('api-key-1', $newKey);
// Schlüssel entfernen
$requestSigningService->removeSigningKey('api-key-1');
```
## Integration
### Middleware-Integration
Die Sicherheitsfunktionen sind als Middleware-Komponenten implementiert und können einfach in die Anwendung integriert werden:
```php
// In der Bootstrap-Datei
$app->addMiddleware(SecurityHeaderMiddleware::class);
$app->addMiddleware(CsrfProtectionMiddleware::class);
$app->addMiddleware(RequestSigningMiddleware::class);
$app->addMiddleware(SessionSecurityMiddleware::class);
```
### Event-Integration
Sicherheitsereignisse werden über das Event-System des Frameworks verarbeitet:
```php
// Event-Listener registrieren
$eventDispatcher->addListener(SecurityEventInterface::class, function (SecurityEventInterface $event) {
// Auf Sicherheitsereignis reagieren
if ($event->getSeverity() >= SecurityLogLevel::WARNING) {
$this->notificationService->sendAlert($event);
}
});
```
### Analytics-Integration
Sicherheitsereignisse werden automatisch im Analytics-System erfasst:
```php
// In der Bootstrap-Datei
$app->addAnalyticsBridge(SecurityAnalyticsListener::class);
// Sicherheitsanalysen abrufen
$securityStats = $analyticsDashboard->getSecurityStats();
```
## Erweiterte Funktionen
### IP-basierte Sicherheit
```php
// IP-Adresse prüfen
if (!$ipSecurityService->isAllowed($request->getClientIp())) {
throw new SecurityException('Zugriff verweigert');
}
// Rate-Limiting basierend auf IP-Adresse
$ipSecurityService->trackRequest($request->getClientIp());
if ($ipSecurityService->isRateLimited($request->getClientIp())) {
throw new SecurityException('Rate-Limit überschritten');
}
```
### Benutzerdefinierte Security-Header
```php
// Eigene Security-Header-Konfiguration erstellen
$config = new SecurityHeaderConfig();
$config->contentSecurityPolicy = "default-src 'self'; script-src 'self' https://trusted-cdn.com;";
$config->frameOptions = "DENY";
$config->referrerPolicy = "strict-origin-when-cross-origin";
// Konfiguration anwenden
$securityHeaderMiddleware = new SecurityHeaderMiddleware($responseManipulator, $config);
```
### Erweiterte CSRF-Schutzmaßnahmen
```php
// CSRF-Token mit Metadaten generieren
$csrfTokenData = new CsrfTokenData(
$csrfToken,
$request->getClientIp(),
$request->path,
new \DateTimeImmutable('+1 hour')
);
// Token mit Metadaten validieren
if (!$csrfValidator->validate($csrfTokenData, $request)) {
throw new SecurityException('Ungültiges oder abgelaufenes CSRF-Token');
}
```
## Fehlerbehebung
### Häufige Probleme
1. **CSP-Fehler im Browser**:
- Überprüfen Sie die Content-Security-Policy in der Konfiguration
- Fügen Sie fehlende Quellen zur CSP hinzu
- Verwenden Sie die Browser-Entwicklertools, um CSP-Verstöße zu identifizieren
2. **CSRF-Token-Fehler**:
- Stellen Sie sicher, dass das Token korrekt im Formular übermittelt wird
- Überprüfen Sie die Session-Konfiguration
- Prüfen Sie, ob das Token abgelaufen ist
3. **Request-Signing-Fehler**:
- Überprüfen Sie, ob der richtige Schlüssel verwendet wird
- Stellen Sie sicher, dass die Uhrzeit auf Client und Server synchronisiert ist
- Prüfen Sie, ob alle erforderlichen Header signiert werden
## Weiterführende Informationen
- [CSRF-Schutz im Detail](csrf-protection.md)
- [Security Headers und CSP](security-headers.md)
- [Request Signing API](request-signing.md)
- [Sicherheits-Best-Practices](/guides/security-best-practices.md)

View File

@@ -0,0 +1,410 @@
# Request Signing
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung des Request Signing korrekt dar.
## Übersicht
Request Signing ist ein Sicherheitsmechanismus, der die Authentizität und Integrität von HTTP-Anfragen gewährleistet. Durch die kryptografische Signierung von Anfragen können Empfänger überprüfen, dass die Anfrage von einem autorisierten Absender stammt und während der Übertragung nicht manipuliert wurde. Das Framework bietet eine robuste Implementierung von Request Signing, die sowohl für ausgehende als auch für eingehende Anfragen verwendet werden kann.
## Hauptkomponenten
### RequestSigningService
Die `RequestSigningService`-Klasse ist die zentrale Komponente für Request Signing:
```php
use App\Framework\Security\RequestSigning\RequestSigningService;
// Service initialisieren
$service = new RequestSigningService(
$signer,
$verifier,
$keyRepository,
$config,
$logger
);
// Ausgehende Anfrage signieren
$signedRequest = $service->signOutgoingRequest($request, $keyId);
// Eingehende Anfrage verifizieren
$result = $service->verifyIncomingRequest($request);
if (!$result->isSuccess()) {
throw new SecurityException('Ungültige Anfrage-Signatur: ' . $result->errorMessage);
}
```
### SigningKey
Die `SigningKey`-Klasse repräsentiert einen kryptografischen Schlüssel für das Signieren von Anfragen:
```php
use App\Framework\Security\RequestSigning\SigningKey;
use App\Framework\Security\RequestSigning\SigningAlgorithm;
// HMAC-Schlüssel generieren
$hmacKey = SigningKey::generateHmac(
'api-key-1',
SigningAlgorithm::HMAC_SHA256,
new \DateTimeImmutable('+30 days') // Ablaufdatum
);
// RSA-Schlüssel erstellen
$privateKey = file_get_contents('private_key.pem');
$rsaKey = SigningKey::createRsa(
'api-key-2',
$privateKey,
new \DateTimeImmutable('+1 year') // Ablaufdatum
);
```
### SigningAlgorithm
Das `SigningAlgorithm`-Enum definiert die unterstützten Signaturalgorithmen:
```php
use App\Framework\Security\RequestSigning\SigningAlgorithm;
// Verfügbare Algorithmen
$algorithm = SigningAlgorithm::HMAC_SHA256; // HMAC mit SHA-256
$algorithm = SigningAlgorithm::HMAC_SHA512; // HMAC mit SHA-512
$algorithm = SigningAlgorithm::RSA_SHA256; // RSA mit SHA-256
$algorithm = SigningAlgorithm::RSA_SHA512; // RSA mit SHA-512
```
### RequestSigner
Die `RequestSigner`-Klasse ist für das Signieren von ausgehenden Anfragen verantwortlich:
```php
use App\Framework\Security\RequestSigning\RequestSigner;
// Signer initialisieren
$signer = new RequestSigner($clock);
// Anfrage signieren
$signedRequest = $signer->signRequest(
$request,
$signingKey,
['(request-target)', 'host', 'date', 'content-type'],
300 // Gültigkeitsdauer in Sekunden
);
```
### RequestVerifier
Die `RequestVerifier`-Klasse ist für die Überprüfung von eingehenden Anfragen verantwortlich:
```php
use App\Framework\Security\RequestSigning\RequestVerifier;
// Verifier initialisieren
$verifier = new RequestVerifier($keyRepository, $clock);
// Anfrage verifizieren
$result = $verifier->verify($request);
if ($result->isSuccess()) {
// Anfrage ist gültig
$signature = $result->signature;
$key = $result->key;
} else {
// Anfrage ist ungültig
$errorMessage = $result->errorMessage;
}
```
### SigningKeyRepository
Das `SigningKeyRepository`-Interface definiert die Methoden zum Speichern und Abrufen von Signaturschlüsseln:
```php
use App\Framework\Security\RequestSigning\SigningKeyRepository;
use App\Framework\Security\RequestSigning\InMemorySigningKeyRepository;
use App\Framework\Security\RequestSigning\EntityManagerSigningKeyRepository;
// In-Memory-Repository für Tests
$repository = new InMemorySigningKeyRepository();
// Entity-Manager-Repository für Produktion
$repository = new EntityManagerSigningKeyRepository($entityManager);
// Schlüssel speichern
$repository->store($signingKey);
// Schlüssel abrufen
$key = $repository->findByKeyId('api-key-1');
// Alle aktiven Schlüssel abrufen
$activeKeys = $repository->getAllActive();
// Schlüssel entfernen
$repository->remove('api-key-1');
// Schlüssel rotieren
$repository->rotateKey('api-key-1', $newKey);
```
## Signaturformat
Das Framework verwendet das [HTTP Signature](https://tools.ietf.org/html/draft-cavage-http-signatures) Format für Request Signing:
```
Authorization: Signature keyId="api-key-1",algorithm="hmac-sha256",headers="(request-target) host date",signature="Base64(HMAC-SHA256(SigningString))"
```
Die Signatur wird aus einer Zeichenkette (Signing String) generiert, die aus den angegebenen Header-Werten besteht:
```
(request-target): post /api/data
host: example.com
date: Tue, 07 Jun 2023 20:51:35 GMT
```
## Verwendung
### Ausgehende Anfragen signieren
```php
// In einem API-Client
public function sendRequest(HttpRequest $request): HttpResponse
{
// Anfrage signieren
$signedRequest = $this->requestSigningService->signOutgoingRequest(
$request,
'api-key-1', // Optional: Spezifischer Schlüssel
['(request-target)', 'host', 'date', 'content-type'], // Optional: Zu signierende Header
300 // Optional: Gültigkeitsdauer in Sekunden
);
// Signierte Anfrage senden
return $this->httpClient->send($signedRequest);
}
```
### Eingehende Anfragen verifizieren
```php
// In einer Middleware
public function __invoke(MiddlewareContext $context, Next $next): MiddlewareContext
{
$request = $context->request;
// Prüfen, ob die Route signierte Anfragen erfordert
if ($this->requiresSignature($request->path)) {
$result = $this->requestSigningService->verifyIncomingRequest($request);
if (!$result->isSuccess()) {
return $context->withResponse(
new Response(401, [], ['error' => 'Ungültige Signatur: ' . $result->errorMessage])
);
}
}
return $next($context);
}
```
### Schlüsselverwaltung
```php
// HMAC-Schlüssel generieren
$key = $requestSigningService->generateHmacKey(
'api-key-1',
SigningAlgorithm::HMAC_SHA256,
new \DateTimeImmutable('+30 days')
);
// RSA-Schlüssel erstellen
$privateKey = file_get_contents('private_key.pem');
$key = $requestSigningService->createRsaKey(
'api-key-2',
$privateKey,
new \DateTimeImmutable('+1 year')
);
// Schlüssel rotieren
$newKey = SigningKey::generateHmac('api-key-1-new', SigningAlgorithm::HMAC_SHA256);
$requestSigningService->rotateSigningKey('api-key-1', $newKey);
// Schlüssel entfernen
$requestSigningService->removeSigningKey('api-key-1');
// Alle aktiven Schlüssel abrufen
$activeKeys = $requestSigningService->getActiveKeys();
```
## Integration mit dem Framework
### RequestSigningMiddleware
Das Framework bietet eine `RequestSigningMiddleware`, die automatisch eingehende Anfragen verifiziert:
```php
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(RequestSigningMiddleware::class);
```
### HttpClientSigningMiddleware
Für ausgehende Anfragen bietet das Framework eine `HttpClientSigningMiddleware`:
```php
// HTTP-Client mit Signatur-Middleware konfigurieren
$httpClient = new HttpClient([
'middlewares' => [
new HttpClientSigningMiddleware($requestSigningService)
]
]);
// Anfragen werden automatisch signiert
$response = $httpClient->send($request);
```
### Konfiguration
Die Request-Signing-Funktionen können in der Konfigurationsdatei angepasst werden:
```php
// config/security.php
return [
'request_signing' => [
'enabled' => true,
'default_algorithm' => 'hmac-sha256',
'default_headers' => ['(request-target)', 'host', 'date', 'content-type'],
'default_expiry' => 300, // Sekunden
'required_routes' => [
'/api/admin/*',
'/api/payments/*',
],
],
];
```
## Erweiterte Funktionen
### Signatur mit Ablaufdatum
Signaturen können mit einem Ablaufdatum versehen werden, um Replay-Angriffe zu verhindern:
```php
// Anfrage mit Ablaufdatum signieren
$signedRequest = $requestSigningService->signOutgoingRequest(
$request,
'api-key-1',
['(request-target)', 'host', 'date', '(created)', '(expires)'],
300 // Gültigkeitsdauer in Sekunden
);
```
Die Signatur enthält dann zusätzliche Felder:
```
Authorization: Signature keyId="api-key-1",algorithm="hmac-sha256",headers="(request-target) host date (created) (expires)",created=1623094295,expires=1623094595,signature="..."
```
### Digest-Header
Für zusätzliche Sicherheit kann der Anfrage-Body mit einem Digest-Header geschützt werden:
```php
// Anfrage mit Digest-Header signieren
$request = $request->withHeader('Digest', 'SHA-256=' . base64_encode(hash('sha256', $request->body, true)));
$signedRequest = $requestSigningService->signOutgoingRequest(
$request,
'api-key-1',
['(request-target)', 'host', 'date', 'digest']
);
```
### Schlüsselrotation
Für zusätzliche Sicherheit sollten Schlüssel regelmäßig rotiert werden:
```php
// Neuen Schlüssel generieren
$newKey = $requestSigningService->generateHmacKey(
'api-key-1-new',
SigningAlgorithm::HMAC_SHA256
);
// Alten Schlüssel durch neuen ersetzen
$requestSigningService->rotateSigningKey('api-key-1', $newKey);
// Alten Schlüssel nach einer Übergangszeit entfernen
// (nachdem alle Clients auf den neuen Schlüssel umgestellt wurden)
$requestSigningService->removeSigningKey('api-key-1');
```
## Sicherheitsüberlegungen
### Best Practices
1. **Verwenden Sie starke Algorithmen**: HMAC-SHA256 oder RSA-SHA256 sind empfohlen.
2. **Schützen Sie private Schlüssel**: Speichern Sie private Schlüssel sicher und teilen Sie sie nie öffentlich.
3. **Signieren Sie kritische Header**: Mindestens `(request-target)`, `host`, `date` und `content-type` sollten signiert werden.
4. **Verwenden Sie Ablaufdaten**: Sowohl für Schlüssel als auch für Signaturen, um Replay-Angriffe zu verhindern.
5. **Rotieren Sie Schlüssel regelmäßig**: Implementieren Sie einen Prozess zur regelmäßigen Schlüsselrotation.
### Bekannte Einschränkungen
- Request Signing schützt nicht vor Man-in-the-Middle-Angriffen; verwenden Sie immer HTTPS.
- Die Sicherheit hängt von der sicheren Speicherung und Verteilung der Schlüssel ab.
- Uhrzeitsynchronisation zwischen Client und Server ist wichtig für zeitbasierte Validierung.
## Fehlerbehebung
### Häufige Probleme
#### Signaturvalidierung schlägt fehl
Mögliche Ursachen:
- Die Signatur ist abgelaufen
- Die Uhrzeit zwischen Client und Server ist nicht synchronisiert
- Der falsche Schlüssel wird verwendet
- Die Header-Liste stimmt nicht überein
- Der Anfrage-Body wurde nach der Signierung geändert
Lösung:
- Überprüfen Sie die Uhrzeit auf Client und Server
- Stellen Sie sicher, dass der richtige Schlüssel verwendet wird
- Überprüfen Sie, ob alle erforderlichen Header signiert werden
- Verwenden Sie den Digest-Header für Anfragen mit Body
#### Schlüssel nicht gefunden
Wenn der Schlüssel nicht gefunden wird:
```php
// Prüfen, ob der Schlüssel existiert
$key = $keyRepository->findByKeyId($keyId);
if ($key === null) {
// Schlüssel nicht gefunden
$this->logger->warning('Signing key not found', ['key_id' => $keyId]);
}
```
#### Logging für Fehlerbehebung
Das Framework bietet umfangreiches Logging für Request Signing:
```php
// In der RequestSigningService-Klasse
$this->logger->info('Request signature verified successfully', [
'key_id' => $result->signature->keyId,
'algorithm' => $result->signature->algorithm->value,
'path' => $request->path,
]);
$this->logger->warning('Request signature verification failed', [
'error' => $result->errorMessage,
'path' => $request->path,
]);
```
## Weiterführende Informationen
- [Security Features Übersicht](index.md)
- [CSRF-Schutz](csrf-protection.md)
- [Security Headers](security-headers.md)
- [Sicherheits-Best-Practices](/guides/security-best-practices.md)
- [HTTP Signatures Spezifikation](https://tools.ietf.org/html/draft-cavage-http-signatures)

View File

@@ -0,0 +1,382 @@
# Security Headers
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der Security Headers korrekt dar.
## Übersicht
Security Headers sind HTTP-Header, die dazu beitragen, die Sicherheit Ihrer Webanwendung zu verbessern, indem sie dem Browser Anweisungen geben, wie er mit bestimmten Sicherheitsaspekten umgehen soll. Das Framework implementiert automatisch eine umfassende Sammlung von Security Headers, die den aktuellen Best Practices entsprechen und Ihre Anwendung vor verschiedenen Arten von Angriffen schützen.
## Hauptkomponenten
### SecurityHeaderMiddleware
Die `SecurityHeaderMiddleware` ist die zentrale Komponente für die Implementierung von Security Headers:
```php
use App\Framework\Http\Middlewares\SecurityHeaderMiddleware;
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(SecurityHeaderMiddleware::class);
```
Diese Middleware:
1. Entfernt unsichere Header wie `X-Powered-By` und `Server`
2. Fügt automatisch wichtige Security Headers zu allen HTTP-Antworten hinzu
3. Passt die Header basierend auf der Umgebung (Entwicklung oder Produktion) an
### SecurityHeaderConfig
Die `SecurityHeaderConfig`-Klasse enthält die Konfiguration für die Security Headers:
```php
use App\Framework\Http\Middlewares\SecurityHeaderConfig;
// Vordefinierte Konfigurationen verwenden
$config = SecurityHeaderConfig::forProduction(); // Strenge Einstellungen für Produktion
$config = SecurityHeaderConfig::forDevelopment(); // Weniger strenge Einstellungen für Entwicklung
// Benutzerdefinierte Konfiguration erstellen
$config = new SecurityHeaderConfig(
$contentSecurityPolicy,
$hstsHeader,
$frameOptions,
$referrerPolicy,
$permissionsPolicy,
$crossOriginEmbedderPolicy,
$crossOriginOpenerPolicy,
$crossOriginResourcePolicy
);
```
## Implementierte Security Headers
### Content-Security-Policy (CSP)
Die Content Security Policy schützt vor Cross-Site Scripting (XSS) und anderen Code-Injection-Angriffen, indem sie definiert, welche Ressourcen geladen werden dürfen:
```
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';
```
In der Produktionsumgebung wird eine strengere CSP verwendet:
```
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';
```
### Strict-Transport-Security (HSTS)
HSTS zwingt den Browser, nur HTTPS-Verbindungen zu verwenden, selbst wenn der Benutzer versucht, HTTP zu verwenden:
```
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
```
Parameter:
- `max-age`: Dauer in Sekunden, für die der Browser nur HTTPS verwenden soll (1 Jahr)
- `includeSubDomains`: Gilt auch für alle Subdomains
- `preload`: Ermöglicht die Aufnahme in die HSTS-Preload-Liste der Browser
### X-Frame-Options
Schützt vor Clickjacking-Angriffen, indem es kontrolliert, ob die Seite in einem Frame eingebettet werden darf:
```
X-Frame-Options: DENY
```
Optionen:
- `DENY`: Die Seite darf nicht in einem Frame eingebettet werden
- `SAMEORIGIN`: Die Seite darf nur in einem Frame auf derselben Domain eingebettet werden
### X-Content-Type-Options
Verhindert MIME-Sniffing, bei dem Browser den Inhaltstyp einer Ressource erraten:
```
X-Content-Type-Options: nosniff
```
### Referrer-Policy
Kontrolliert, welche Referrer-Informationen beim Navigieren zu anderen Seiten gesendet werden:
```
Referrer-Policy: strict-origin-when-cross-origin
```
Optionen:
- `no-referrer`: Keine Referrer-Informationen senden
- `no-referrer-when-downgrade`: Keine Referrer-Informationen senden, wenn von HTTPS zu HTTP navigiert wird
- `same-origin`: Nur Referrer-Informationen senden, wenn auf derselben Domain navigiert wird
- `strict-origin`: Nur die Ursprungs-URL senden und nur bei gleichem Sicherheitsniveau
- `strict-origin-when-cross-origin`: Vollständige URL bei gleicher Domain, nur Ursprung bei Cross-Origin und gleichem Sicherheitsniveau
### Permissions-Policy
Kontrolliert, welche Browser-Features und APIs die Seite verwenden darf:
```
Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=()
```
Diese Einstellung deaktiviert den Zugriff auf Kamera, Mikrofon, Geolocation und FLoC (Federated Learning of Cohorts).
### Cross-Origin-Policies
Eine Gruppe von Headern, die die Interaktion zwischen verschiedenen Ursprüngen kontrollieren:
```
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
```
Diese Header schützen vor verschiedenen Cross-Origin-Angriffen und Informationslecks.
### X-Permitted-Cross-Domain-Policies
Kontrolliert, ob Cross-Domain-Richtliniendateien (crossdomain.xml) geladen werden dürfen:
```
X-Permitted-Cross-Domain-Policies: none
```
## Konfiguration
### Umgebungsspezifische Konfiguration
Das Framework bietet vordefinierte Konfigurationen für verschiedene Umgebungen:
```php
// In der SecurityHeaderMiddleware
$config = $this->securityConfig->enableStrictMode
? SecurityHeaderConfig::forProduction()
: SecurityHeaderConfig::forDevelopment();
```
Die `enableStrictMode`-Einstellung kann in der Konfigurationsdatei angepasst werden:
```php
// config/security.php
return [
'headers' => [
'strict_mode' => true, // Produktionsmodus mit strengeren Einstellungen
'content_security_policy' => "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';",
'hsts' => [
'enabled' => true,
'max_age' => 31536000, // 1 Jahr
'include_subdomains' => true,
'preload' => true,
],
],
];
```
### Benutzerdefinierte Konfiguration
Sie können die Security Headers auch manuell konfigurieren:
```php
// Eigene Security-Header-Konfiguration erstellen
$config = new SecurityHeaderConfig();
$config->contentSecurityPolicy = "default-src 'self'; script-src 'self' https://trusted-cdn.com;";
$config->frameOptions = "DENY";
$config->referrerPolicy = "strict-origin-when-cross-origin";
// Konfiguration anwenden
$securityHeaderMiddleware = new SecurityHeaderMiddleware($responseManipulator, $config);
```
## Verwendung
### Automatische Anwendung
Die Security Headers werden automatisch auf alle Antworten angewendet, wenn die Middleware registriert ist:
```php
// In der Bootstrap-Datei
$app->addMiddleware(SecurityHeaderMiddleware::class);
```
### Anpassung für bestimmte Routen
In einigen Fällen möchten Sie möglicherweise die Security Headers für bestimmte Routen anpassen:
```php
// In einem Controller oder einer Middleware
public function __invoke(MiddlewareContext $context, Next $next): MiddlewareContext
{
$resultContext = $next($context);
if ($resultContext->hasResponse()) {
$response = $resultContext->response;
$headers = $response->headers;
// CSP für eine bestimmte Route anpassen
$updatedHeaders = $headers->with(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' https://trusted-cdn.com;"
);
$updatedResponse = $this->manipulator->withHeaders($response, $updatedHeaders);
return $resultContext->withResponse($updatedResponse);
}
return $resultContext;
}
```
### Prüfen der aktuellen Security Headers
Sie können die aktuellen Security Headers Ihrer Anwendung mit verschiedenen Tools überprüfen:
1. **Browser-Entwicklertools**: Überprüfen Sie die Response-Header in der Netzwerk-Ansicht
2. **Online-Tools**: Verwenden Sie Dienste wie [securityheaders.com](https://securityheaders.com) oder [observatory.mozilla.org](https://observatory.mozilla.org)
3. **Curl-Befehl**: `curl -I https://yourdomain.com`
## Content Security Policy (CSP)
### Grundlegende CSP-Direktiven
Die Content Security Policy ist einer der wichtigsten Security Headers und verdient besondere Aufmerksamkeit:
- `default-src`: Standardrichtlinie für alle Ressourcentypen
- `script-src`: Erlaubte Quellen für JavaScript
- `style-src`: Erlaubte Quellen für CSS
- `img-src`: Erlaubte Quellen für Bilder
- `font-src`: Erlaubte Quellen für Schriftarten
- `connect-src`: Erlaubte Ziele für Fetch, XHR, WebSocket
- `media-src`: Erlaubte Quellen für Audio und Video
- `object-src`: Erlaubte Quellen für Plugins (Flash, PDF)
- `frame-src`: Erlaubte Quellen für Frames
- `base-uri`: Erlaubte URLs für das base-Element
- `form-action`: Erlaubte Ziele für Formularübermittlungen
- `frame-ancestors`: Erlaubte übergeordnete Dokumente (ähnlich X-Frame-Options)
### CSP-Quellwerte
- `'self'`: Ressourcen von derselben Ursprungs-Domain
- `'none'`: Keine Ressourcen erlaubt
- `'unsafe-inline'`: Inline-Skripte und Styles erlaubt (vermeiden in Produktion)
- `'unsafe-eval'`: eval() und ähnliche Funktionen erlaubt (vermeiden in Produktion)
- `'nonce-<base64-value>'`: Ressourcen mit entsprechendem Nonce-Attribut
- `'sha256-<hash>'`: Ressourcen mit entsprechendem Hash
- `https://example.com`: Spezifische Domain
- `https://*.example.com`: Alle Subdomains
- `data:`: Data-URLs (vorsichtig verwenden)
### CSP-Beispiele
Strenge CSP für Produktion:
```
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';
```
CSP mit externen Ressourcen:
```
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https://img.example.com;
```
CSP mit Nonce für Inline-Skripte:
```php
// In einem Controller
public function showPage(): Response
{
$nonce = bin2hex(random_bytes(16));
// CSP mit Nonce setzen
$csp = "default-src 'self'; script-src 'self' 'nonce-{$nonce}';";
return $this->render('page.twig', [
'nonce' => $nonce,
'csp' => $csp,
]);
}
```
In der Vorlage:
```html
<script nonce="{{ nonce }}">
// Dieser Inline-Code ist erlaubt, weil er den korrekten Nonce hat
console.log('Hello, world!');
</script>
```
### CSP-Reporting
Sie können CSP-Verstöße an einen Endpunkt melden lassen:
```
Content-Security-Policy: default-src 'self'; report-uri /csp-report;
```
Oder verwenden Sie den neueren Report-To-Header:
```
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"/csp-report"}]}
```
## Fehlerbehebung
### Häufige Probleme
#### CSP blockiert legitime Ressourcen
Wenn Ihre Anwendung nicht korrekt funktioniert, weil die CSP legitime Ressourcen blockiert:
1. Überprüfen Sie die Konsole im Browser auf CSP-Verstöße
2. Erweitern Sie die CSP um die benötigten Quellen
3. Verwenden Sie vorübergehend eine weniger strenge CSP während der Entwicklung
```php
// Weniger strenge CSP für Entwicklung
$config->contentSecurityPolicy = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
```
#### HSTS-Probleme
Wenn Sie HSTS aktiviert haben und Probleme auftreten:
1. Beginnen Sie mit einer kurzen `max-age` (z.B. 300 Sekunden)
2. Aktivieren Sie `includeSubDomains` erst, wenn Sie sicher sind, dass alle Subdomains HTTPS unterstützen
3. Verwenden Sie `preload` nur, wenn Sie sicher sind, dass Ihre Domain dauerhaft HTTPS unterstützt
#### Frames werden blockiert
Wenn Ihre Anwendung Frames verwenden muss:
```php
// X-Frame-Options anpassen
$config->frameOptions = "SAMEORIGIN"; // Erlaubt Frames auf derselben Domain
```
## Sicherheitsüberlegungen
### Best Practices
1. **Verwenden Sie strenge CSP**: Vermeiden Sie 'unsafe-inline' und 'unsafe-eval' in Produktion
2. **Aktivieren Sie HSTS**: Erzwingen Sie HTTPS für alle Verbindungen
3. **Regelmäßige Überprüfung**: Testen Sie Ihre Security Headers regelmäßig mit Tools wie [securityheaders.com](https://securityheaders.com)
4. **Umgebungsspezifische Konfiguration**: Verwenden Sie unterschiedliche Konfigurationen für Entwicklung und Produktion
5. **CSP-Reporting**: Implementieren Sie CSP-Reporting, um Verstöße zu erkennen
### Bekannte Einschränkungen
- Ältere Browser unterstützen möglicherweise nicht alle Security Headers
- Eine zu strenge CSP kann die Funktionalität von Drittanbieter-Skripten beeinträchtigen
- HSTS kann nicht rückgängig gemacht werden, bis die `max-age` abgelaufen ist
## Weiterführende Informationen
- [Security Features Übersicht](index.md)
- [CSRF-Schutz](csrf-protection.md)
- [Request Signing API](request-signing.md)
- [Sicherheits-Best-Practices](/guides/security-best-practices.md)
- [MDN Web Security](https://developer.mozilla.org/en-US/docs/Web/Security)
- [OWASP Security Headers](https://owasp.org/www-project-secure-headers/)

View File

@@ -0,0 +1,424 @@
# Validation Framework Beispiele
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung des Validation Frameworks korrekt dar.
## Übersicht
Diese Dokumentation enthält praktische Beispiele für die Verwendung des Validation Frameworks in verschiedenen Szenarien. Die Beispiele zeigen, wie Sie Validierungsregeln auf Objekteigenschaften anwenden, Validierungsergebnisse verarbeiten und das Framework in verschiedenen Kontexten integrieren können.
## Grundlegende Verwendung
### Einfache Objektvalidierung
```php
use App\Framework\Validation\Rules\Required;
use App\Framework\Validation\Rules\Email;
use App\Framework\Validation\Rules\StringLength;
// 1. Definieren Sie eine Klasse mit Validierungsregeln
class UserData
{
#[Required]
#[StringLength(min: 3, max: 50)]
public string $name;
#[Required]
#[Email]
public string $email;
#[StringLength(max: 500)]
public ?string $bio = null;
}
// 2. Erstellen Sie eine Instanz der Klasse
$userData = new UserData();
$userData->name = 'Max Mustermann';
$userData->email = 'max@example.com';
// 3. Validieren Sie das Objekt
$validator = $container->get(Validator::class);
$result = $validator->validate($userData);
// 4. Überprüfen Sie das Ergebnis
if ($result->hasErrors()) {
// Fehlerbehandlung
$errors = $result->getAllErrorMessages();
foreach ($errors as $error) {
echo $error . "\n";
}
} else {
// Erfolgreiche Validierung
echo "Validierung erfolgreich!";
}
```
### Validierung mit benutzerdefinierten Fehlermeldungen
```php
class ProductData
{
#[Required(message: "Der Produktname ist erforderlich.")]
#[StringLength(min: 3, max: 100, message: "Der Produktname muss zwischen 3 und 100 Zeichen lang sein.")]
public string $name;
#[Required(message: "Der Preis ist erforderlich.")]
#[Range(min: 0, message: "Der Preis muss größer oder gleich 0 sein.")]
public float $price;
#[Required(message: "Die Kategorie ist erforderlich.")]
#[In(values: ['electronics', 'books', 'clothing'], message: "Ungültige Kategorie.")]
public string $category;
}
```
### Zugriff auf feldspezifische Fehler
```php
$result = $validator->validate($userData);
if ($result->hasErrors()) {
// Alle Fehler für ein bestimmtes Feld abrufen
$emailErrors = $result->getFieldErrors('email');
foreach ($emailErrors as $error) {
echo "Fehler im Feld 'email': $error\n";
}
// Alle Fehler nach Feld gruppiert abrufen
$allErrors = $result->getAll();
foreach ($allErrors as $field => $errors) {
echo "Fehler im Feld '$field':\n";
foreach ($errors as $error) {
echo "- $error\n";
}
}
}
```
## Fortgeschrittene Verwendung
### Validierungsgruppen
Validierungsgruppen ermöglichen es, verschiedene Validierungsszenarien für dasselbe Objekt zu definieren:
```php
use App\Framework\Validation\Rules\Required;
use App\Framework\Validation\Rules\Email;
use App\Framework\Validation\Rules\StringLength;
class UserProfile
{
#[Required(groups: ['registration', 'profile'])]
#[StringLength(min: 3, max: 50)]
public string $name;
#[Required(groups: ['registration'])]
#[Email]
public string $email;
#[Required(groups: ['profile'])]
#[StringLength(max: 500)]
public ?string $bio = null;
#[Required(groups: ['profile'])]
#[Url]
public ?string $website = null;
}
// Validierung mit einer bestimmten Gruppe
$result = $validator->validate($userProfile, 'registration');
// In diesem Fall werden nur die Regeln angewendet, die zur Gruppe 'registration' gehören
// oder keine Gruppe spezifiziert haben
```
### Verschachtelte Validierung
Validierung von Objekten, die andere validierbare Objekte enthalten:
```php
use App\Framework\Validation\Rules\Required;
use App\Framework\Validation\Rules\Email;
class Address
{
#[Required]
#[StringLength(min: 3, max: 100)]
public string $street;
#[Required]
#[StringLength(min: 3, max: 50)]
public string $city;
#[Required]
#[StringLength(min: 5, max: 10)]
public string $zipCode;
}
class Customer
{
#[Required]
#[StringLength(min: 3, max: 50)]
public string $name;
#[Required]
#[Email]
public string $email;
#[Required]
public Address $address;
}
// Validierung eines Objekts mit verschachtelten Objekten
$address = new Address();
$address->street = 'Musterstraße 123';
$address->city = 'Berlin';
$address->zipCode = '10115';
$customer = new Customer();
$customer->name = 'Max Mustermann';
$customer->email = 'max@example.com';
$customer->address = $address;
// Validierung des Hauptobjekts
$result = $validator->validate($customer);
// Manuelle Validierung des verschachtelten Objekts
$addressResult = $validator->validate($customer->address);
$result->merge($addressResult); // Kombinieren der Ergebnisse
```
### Benutzerdefinierte Validierungslogik
Verwendung der `Custom`-Regel für komplexe Validierungsanforderungen:
```php
use App\Framework\Validation\Rules\Custom;
use App\Framework\Validation\Rules\Required;
class PasswordReset
{
#[Required]
public string $email;
#[Required]
public string $password;
#[Required]
#[Custom(callback: 'validatePasswordConfirmation', message: "Die Passwörter stimmen nicht überein.")]
public string $passwordConfirmation;
private function validatePasswordConfirmation($value): bool
{
return $value === $this->password;
}
}
```
## Integration mit Formularen
### Formularvalidierung
Verwendung des Validation Frameworks zur Validierung von Formulardaten:
```php
use App\Framework\Validation\ValidationFormHandler;
use App\Framework\Http\Response;
class UserController
{
public function handleRegistration(
Request $request,
ValidationFormHandler $formHandler,
UserService $userService
): Response {
// Formulardaten abrufen
$formData = $request->getFormData();
// Validierung durchführen
$result = $formHandler->validate(UserRegistration::class, $formData);
if ($result->hasErrors()) {
// Formular mit Fehlern neu rendern
return $this->renderForm('registration', [
'errors' => $result->getAll(),
'data' => $formData
]);
}
// Erfolgreiche Validierung, Benutzer registrieren
$userService->registerUser($formData);
// Weiterleitung zur Erfolgsseite
return $this->redirect('/registration/success');
}
}
```
### Formularvorlage mit Fehleranzeige
```html
<form method="post" action="/register">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="<?= htmlspecialchars($data['name'] ?? '') ?>">
<?php if (isset($errors['name'])): ?>
<div class="error">
<?= htmlspecialchars($errors['name'][0]) ?>
</div>
<?php endif; ?>
</div>
<div class="form-group">
<label for="email">E-Mail:</label>
<input type="email" id="email" name="email" value="<?= htmlspecialchars($data['email'] ?? '') ?>">
<?php if (isset($errors['email'])): ?>
<div class="error">
<?= htmlspecialchars($errors['email'][0]) ?>
</div>
<?php endif; ?>
</div>
<div class="form-group">
<label for="password">Passwort:</label>
<input type="password" id="password" name="password">
<?php if (isset($errors['password'])): ?>
<div class="error">
<?= htmlspecialchars($errors['password'][0]) ?>
</div>
<?php endif; ?>
</div>
<button type="submit">Registrieren</button>
</form>
```
## API-Validierung
### Middleware für API-Validierung
Das Framework bietet eine `InputValidationMiddleware` für die automatische Validierung von API-Anfragen:
```php
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(InputValidationMiddleware::class);
```
Konfiguration in `config/validation.php`:
```php
return [
'routes' => [
'/api/users' => [
'POST' => UserCreateData::class,
'PUT' => UserUpdateData::class
],
'/api/products' => [
'POST' => ProductCreateData::class,
'PUT' => ProductUpdateData::class
]
]
];
```
### API-Controller mit Validierung
```php
class ApiController
{
public function createUser(Request $request): Response
{
// Die Validierung wurde bereits durch die Middleware durchgeführt
// Wenn wir hier sind, waren die Daten gültig
$userData = $request->getJsonData();
$user = $this->userService->createUser($userData);
return new Response(201, [], [
'success' => true,
'user' => $user->toArray()
]);
}
}
```
### Fehlerbehandlung für API-Validierung
Die `InputValidationMiddleware` gibt automatisch eine Fehlerantwort zurück, wenn die Validierung fehlschlägt:
```json
{
"error": "Validation failed",
"validation_errors": {
"name": ["Der Name ist erforderlich."],
"email": ["Bitte geben Sie eine gültige E-Mail-Adresse ein."]
}
}
```
## Leistungsoptimierung
### Validierungs-Cache
Für häufig validierte Objekte kann ein Cache verwendet werden, um die Leistung zu verbessern:
```php
use App\Framework\Validation\ValidationCacheDecorator;
// Cache-Decorator erstellen
$cachedValidator = new ValidationCacheDecorator($validator, $cache);
// Validierung mit Cache durchführen
$result = $cachedValidator->validate($userData);
```
### Validierungsgruppen für partielle Validierung
Verwenden Sie Validierungsgruppen, um nur die relevanten Teile eines Objekts zu validieren:
```php
// Nur die für die Aktualisierung relevanten Felder validieren
$result = $validator->validate($userData, 'update');
```
## Fehlerbehebung
### Häufige Probleme und Lösungen
#### Validierungsregeln werden nicht angewendet
Stellen Sie sicher, dass:
- Die Attribute korrekt definiert sind
- Die Eigenschaften für den Validator zugänglich sind (public oder mit Reflection)
- Der Validator korrekt initialisiert wurde
```php
// Korrekte Initialisierung des Validators
$validator = new Validator($reflectionProvider);
```
#### Falsche Fehlermeldungen
Überprüfen Sie die Reihenfolge der Validierungsregeln:
```php
// Korrekte Reihenfolge: Required vor anderen Regeln
#[Required]
#[Email]
public string $email;
```
#### Leistungsprobleme
Verwenden Sie den ValidationCacheDecorator und vermeiden Sie zu komplexe Validierungsregeln:
```php
// Einfache Regeln verwenden
#[StringLength(max: 255)] // Besser als komplexe reguläre Ausdrücke
public string $name;
```
## Weiterführende Informationen
- [Validation Framework Übersicht](index.md)
- [Verfügbare Validierungsregeln](rules.md)
- [API-Validierung](/guides/api-validation.md)

View File

@@ -0,0 +1,338 @@
# Validation Framework
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung des Validation Frameworks korrekt dar.
## Übersicht
Das Validation Framework ist ein leistungsstarkes System zur Validierung von Objekten und Daten in der Anwendung. Es nutzt moderne PHP-Attribute, um Validierungsregeln direkt an Objekteigenschaften zu definieren, und bietet eine flexible, erweiterbare Architektur für verschiedene Validierungsanforderungen.
## Hauptkomponenten
### Validator-Klasse
Die zentrale Klasse für die Validierungsfunktionalität:
```php
// Klasse initialisieren
$validator = new Validator($reflectionProvider);
// Objekt validieren
$result = $validator->validate($object);
// Validierungsergebnis prüfen
if ($result->hasErrors()) {
// Fehlerbehandlung
$errors = $result->getAllErrorMessages();
}
```
### ValidationRule-Interface
Das Basis-Interface für alle Validierungsregeln:
```php
interface ValidationRule
{
public function validate(mixed $value): bool;
public function getErrorMessages(): array;
}
```
Alle Validierungsregeln implementieren dieses Interface und werden als PHP-Attribute verwendet.
### ValidationResult-Klasse
Speichert und verwaltet Validierungsergebnisse:
- Sammelt Fehlermeldungen für verschiedene Felder
- Bietet Methoden zum Abfragen von Fehlern
- Ermöglicht das Zusammenführen mehrerer Validierungsergebnisse
```php
// Fehler prüfen
if ($result->hasErrors()) {
// Alle Fehlermeldungen abrufen
$allErrors = $result->getAllErrorMessages();
// Fehler für ein bestimmtes Feld abrufen
$fieldErrors = $result->getFieldErrors('email');
}
```
## Validierungsregeln
Das Framework bietet eine Vielzahl vordefinierter Validierungsregeln:
### Grundlegende Regeln
- **Required**: Stellt sicher, dass ein Wert vorhanden ist
- **Email**: Validiert E-Mail-Adressen
- **Url**: Validiert URLs
- **Numeric**: Stellt sicher, dass ein Wert numerisch ist
- **StringLength**: Validiert die Länge eines Strings
### Erweiterte Regeln
- **Pattern**: Validiert Werte gegen reguläre Ausdrücke
- **Range**: Validiert numerische Werte innerhalb eines Bereichs
- **In**: Prüft, ob ein Wert in einer Liste gültiger Werte enthalten ist
- **DateFormat**: Validiert Datumsformate
- **Phone**: Validiert Telefonnummern
- **Ulid**: Validiert ULID-Werte
- **IsTrue**: Prüft, ob ein Wert true ist
### Benutzerdefinierte Regeln
Eigene Validierungsregeln können durch Implementierung des ValidationRule-Interfaces erstellt werden:
```php
#[Attribute(Attribute::TARGET_PROPERTY)]
final readonly class CustomRule implements ValidationRule
{
public function __construct(
private string $param,
private ?string $message = null
) {
}
public function validate(mixed $value): bool
{
// Validierungslogik implementieren
return true; // oder false
}
public function getErrorMessages(): array
{
return [$this->message ?? 'Standardfehlermeldung'];
}
}
```
## Verwendung
### Objekte mit Attributen validieren
```php
class UserData
{
#[Required]
#[StringLength(min: 3, max: 50)]
public string $name;
#[Required]
#[Email]
public string $email;
#[StringLength(max: 500)]
public ?string $bio = null;
#[Range(min: 18)]
public ?int $age = null;
}
// Validierung durchführen
$userData = new UserData();
$userData->name = 'Max';
$userData->email = 'invalid-email';
$result = $validator->validate($userData);
```
### Validierungsgruppen
Das Framework unterstützt Validierungsgruppen, um verschiedene Validierungsszenarien zu ermöglichen:
```php
class UserData
{
#[Required(groups: ['registration', 'profile'])]
#[StringLength(min: 3, max: 50)]
public string $name;
#[Required(groups: ['registration'])]
#[Email]
public string $email;
#[Required(groups: ['profile'])]
#[StringLength(max: 500)]
public ?string $bio = null;
}
// Validierung mit Gruppe durchführen
$result = $validator->validate($userData, 'registration');
```
### Fehlerbehandlung
```php
if ($result->hasErrors()) {
foreach ($result->getAll() as $field => $errors) {
echo "Fehler im Feld '$field':\n";
foreach ($errors as $error) {
echo "- $error\n";
}
}
}
```
## Integration
### Formular-Validierung
Das Framework bietet eine `ValidationFormHandler`-Klasse für die Validierung von Formulardaten:
```php
// In einem Controller
public function handleForm(Request $request, ValidationFormHandler $formHandler): Response
{
$formData = $request->getFormData();
$result = $formHandler->validate(UserData::class, $formData);
if ($result->hasErrors()) {
return $this->renderForm('user_form', [
'errors' => $result->getAll(),
'data' => $formData
]);
}
// Erfolgreiche Validierung, Daten verarbeiten
$this->userService->createUser($formData);
return $this->redirect('/success');
}
```
### Middleware-Integration
Das Framework bietet eine `InputValidationMiddleware` für die automatische Validierung von API-Anfragen:
```php
// In der Bootstrap-Datei oder Router-Konfiguration
$app->addMiddleware(InputValidationMiddleware::class);
```
Konfiguration in `config/validation.php`:
```php
return [
'routes' => [
'/api/users' => [
'POST' => UserData::class,
'PUT' => UserUpdateData::class
]
]
];
```
## Erweiterbarkeit
### Eigene Validierungsregeln erstellen
1. Interface implementieren:
```php
#[Attribute(Attribute::TARGET_PROPERTY)]
final readonly class Password implements ValidationRule
{
public function __construct(
private int $minLength = 8,
private bool $requireSpecialChars = true,
private ?string $message = null
) {
}
public function validate(mixed $value): bool
{
if ($value === null || $value === '') {
return true; // Leere Werte werden von Required-Regel behandelt
}
if (!is_string($value) || strlen($value) < $this->minLength) {
return false;
}
if ($this->requireSpecialChars && !preg_match('/[^a-zA-Z0-9]/', $value)) {
return false;
}
return true;
}
public function getErrorMessages(): array
{
return [$this->message ?? "Das Passwort muss mindestens {$this->minLength} Zeichen lang sein und Sonderzeichen enthalten."];
}
}
```
2. Verwendung:
```php
class UserRegistration
{
#[Required]
#[Email]
public string $email;
#[Required]
#[Password(minLength: 10)]
public string $password;
}
```
### Validierungsgruppen implementieren
Für Regeln, die Validierungsgruppen unterstützen sollen, implementieren Sie das `GroupAware`-Interface:
```php
#[Attribute(Attribute::TARGET_PROPERTY)]
final readonly class Required implements ValidationRule, GroupAware
{
/**
* @param array<string> $groups Validierungsgruppen
*/
public function __construct(
private ?string $message = null,
private array $groups = []
) {
}
public function validate(mixed $value): bool
{
return $value !== null && $value !== '';
}
public function getErrorMessages(): array
{
return [$this->message ?? 'Dieser Wert ist erforderlich.'];
}
public function belongsToGroup(string $group): bool
{
return empty($this->groups) || in_array($group, $this->groups);
}
}
```
## Fehlerbehebung
### Häufige Probleme
1. **Validierungsregeln werden nicht angewendet**:
- Stellen Sie sicher, dass die Attribute korrekt definiert sind
- Überprüfen Sie, ob die Eigenschaften für den Validator zugänglich sind (public oder mit Reflection)
2. **Falsche Fehlermeldungen**:
- Überprüfen Sie die Reihenfolge der Validierungsregeln
- Stellen Sie sicher, dass die Regeln für den richtigen Datentyp verwendet werden
3. **Leistungsprobleme**:
- Verwenden Sie ValidationCacheDecorator für häufig validierte Objekte
- Vermeiden Sie zu komplexe Validierungsregeln
## Weiterführende Informationen
- [Verfügbare Validierungsregeln](rules.md)
- [Formular-Integration](examples.md)
- [API-Validierung](/guides/api-validation.md)

View File

@@ -0,0 +1,451 @@
# Validierungsregeln
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der Validierungsregeln korrekt dar.
## Übersicht
Das Validation Framework bietet eine umfangreiche Sammlung vordefinierter Validierungsregeln, die als PHP-Attribute verwendet werden können. Diese Regeln können auf Objekteigenschaften angewendet werden, um Daten zu validieren und sicherzustellen, dass sie den erwarteten Anforderungen entsprechen.
## Grundlegende Regeln
### Required
Die `Required`-Regel stellt sicher, dass ein Wert vorhanden ist und nicht leer ist:
```php
use App\Framework\Validation\Rules\Required;
class UserData
{
#[Required]
public string $username;
#[Required(message: "Die E-Mail-Adresse ist erforderlich.")]
public string $email;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
- `groups` (optional): Array von Validierungsgruppen, zu denen diese Regel gehört
**Validierungslogik:**
- Gibt `false` zurück, wenn der Wert `null` oder ein leerer String ist
- Gibt `true` zurück für alle anderen Werte, einschließlich `0` und `false`
### Email
Die `Email`-Regel validiert, dass ein Wert eine gültige E-Mail-Adresse ist:
```php
use App\Framework\Validation\Rules\Email;
class UserData
{
#[Email]
public string $email;
#[Email(message: "Bitte geben Sie eine gültige E-Mail-Adresse ein.")]
public string $alternativeEmail;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Verwendet `filter_var()` mit `FILTER_VALIDATE_EMAIL` zur Validierung
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### StringLength
Die `StringLength`-Regel validiert die Länge eines Strings:
```php
use App\Framework\Validation\Rules\StringLength;
class UserData
{
#[StringLength(min: 3, max: 50)]
public string $username;
#[StringLength(min: 8, message: "Das Passwort muss mindestens 8 Zeichen lang sein.")]
public string $password;
#[StringLength(max: 500)]
public ?string $bio;
}
```
**Parameter:**
- `min` (optional): Minimale Länge des Strings
- `max` (optional): Maximale Länge des Strings
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Mindestens einer der Parameter `min` oder `max` muss gesetzt sein
- Verwendet `mb_strlen()` für korrekte Behandlung von Multibyte-Zeichen
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### Url
Die `Url`-Regel validiert, dass ein Wert eine gültige URL ist:
```php
use App\Framework\Validation\Rules\Url;
class UserData
{
#[Url]
public string $website;
#[Url(message: "Bitte geben Sie eine gültige URL ein.")]
public string $profileUrl;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Verwendet `filter_var()` mit `FILTER_VALIDATE_URL` zur Validierung
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### Numeric
Die `Numeric`-Regel validiert, dass ein Wert numerisch ist:
```php
use App\Framework\Validation\Rules\Numeric;
class ProductData
{
#[Numeric]
public $price;
#[Numeric(message: "Die Menge muss eine Zahl sein.")]
public $quantity;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Verwendet `is_numeric()` zur Validierung
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
## Erweiterte Regeln
### Pattern
Die `Pattern`-Regel validiert einen Wert gegen einen regulären Ausdruck:
```php
use App\Framework\Validation\Rules\Pattern;
class UserData
{
#[Pattern(pattern: '/^[a-zA-Z0-9_]+$/', message: "Der Benutzername darf nur Buchstaben, Zahlen und Unterstriche enthalten.")]
public string $username;
}
```
**Parameter:**
- `pattern`: Regulärer Ausdruck für die Validierung
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Verwendet `preg_match()` zur Validierung gegen den angegebenen regulären Ausdruck
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### Range
Die `Range`-Regel validiert, dass ein numerischer Wert innerhalb eines bestimmten Bereichs liegt:
```php
use App\Framework\Validation\Rules\Range;
class ProductData
{
#[Range(min: 0)]
public float $price;
#[Range(min: 1, max: 100, message: "Die Menge muss zwischen 1 und 100 liegen.")]
public int $quantity;
}
```
**Parameter:**
- `min` (optional): Minimaler Wert
- `max` (optional): Maximaler Wert
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Mindestens einer der Parameter `min` oder `max` muss gesetzt sein
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### In
Die `In`-Regel validiert, dass ein Wert in einer Liste gültiger Werte enthalten ist:
```php
use App\Framework\Validation\Rules\In;
class ProductData
{
#[In(values: ['small', 'medium', 'large'])]
public string $size;
#[In(values: [1, 2, 3, 4, 5], message: "Bitte wählen Sie eine gültige Kategorie.")]
public int $category;
}
```
**Parameter:**
- `values`: Array gültiger Werte
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Verwendet `in_array()` zur Validierung
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### DateFormat
Die `DateFormat`-Regel validiert, dass ein Wert ein gültiges Datum in einem bestimmten Format ist:
```php
use App\Framework\Validation\Rules\DateFormat;
class EventData
{
#[DateFormat(format: 'Y-m-d')]
public string $date;
#[DateFormat(format: 'Y-m-d H:i:s', message: "Bitte geben Sie ein gültiges Datum und eine gültige Uhrzeit ein.")]
public string $datetime;
}
```
**Parameter:**
- `format`: Das erwartete Datumsformat (PHP date()-Format)
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Verwendet `DateTime::createFromFormat()` zur Validierung
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### Phone
Die `Phone`-Regel validiert, dass ein Wert eine gültige Telefonnummer ist:
```php
use App\Framework\Validation\Rules\Phone;
class ContactData
{
#[Phone]
public string $phoneNumber;
#[Phone(message: "Bitte geben Sie eine gültige Telefonnummer ein.")]
public string $mobileNumber;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Validiert Telefonnummern mit einem flexiblen Muster, das verschiedene Formate unterstützt
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### Ulid
Die `Ulid`-Regel validiert, dass ein Wert ein gültiger ULID (Universally Unique Lexicographically Sortable Identifier) ist:
```php
use App\Framework\Validation\Rules\Ulid;
class EntityData
{
#[Ulid]
public string $id;
#[Ulid(message: "Die ID muss ein gültiger ULID sein.")]
public string $parentId;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Validiert, dass der Wert ein gültiger ULID ist (26 Zeichen, Base32-Kodierung)
- Leere Werte werden als gültig betrachtet (verwenden Sie `Required` in Kombination, um leere Werte zu verhindern)
### IsTrue
Die `IsTrue`-Regel validiert, dass ein Wert `true` ist:
```php
use App\Framework\Validation\Rules\IsTrue;
class RegistrationData
{
#[IsTrue(message: "Sie müssen die Nutzungsbedingungen akzeptieren.")]
public bool $termsAccepted;
}
```
**Parameter:**
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Gibt `true` zurück, wenn der Wert `true` ist
- Gibt `false` zurück für alle anderen Werte
- Leere Werte werden als ungültig betrachtet
## Benutzerdefinierte Regeln
### Custom
Die `Custom`-Regel ermöglicht die Definition einer benutzerdefinierten Validierungslogik mit einer Callback-Funktion:
```php
use App\Framework\Validation\Rules\Custom;
class UserData
{
#[Custom(callback: 'validateUsername', message: "Der Benutzername ist ungültig.")]
public string $username;
private function validateUsername($value): bool
{
// Benutzerdefinierte Validierungslogik
return preg_match('/^[a-zA-Z0-9_]{3,20}$/', $value) === 1;
}
}
```
**Parameter:**
- `callback`: Name der Callback-Methode oder Closure
- `message` (optional): Benutzerdefinierte Fehlermeldung
**Validierungslogik:**
- Ruft die angegebene Callback-Funktion auf und gibt deren Rückgabewert zurück
- Leere Werte werden an die Callback-Funktion übergeben
## Eigene Validierungsregeln erstellen
Sie können eigene Validierungsregeln erstellen, indem Sie das `ValidationRule`-Interface implementieren:
```php
use App\Framework\Validation\ValidationRule;
use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
final readonly class Password implements ValidationRule
{
public function __construct(
private int $minLength = 8,
private bool $requireSpecialChars = true,
private ?string $message = null
) {
}
public function validate(mixed $value): bool
{
if ($value === null || $value === '') {
return true; // Leere Werte werden von Required-Regel behandelt
}
if (!is_string($value) || strlen($value) < $this->minLength) {
return false;
}
if ($this->requireSpecialChars && !preg_match('/[^a-zA-Z0-9]/', $value)) {
return false;
}
return true;
}
public function getErrorMessages(): array
{
return [$this->message ?? "Das Passwort muss mindestens {$this->minLength} Zeichen lang sein und Sonderzeichen enthalten."];
}
}
```
## Validierungsgruppen
Für Regeln, die Validierungsgruppen unterstützen sollen, implementieren Sie das `GroupAware`-Interface:
```php
use App\Framework\Validation\ValidationRule;
use App\Framework\Validation\GroupAware;
use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
final readonly class Required implements ValidationRule, GroupAware
{
/**
* @param array<string> $groups Validierungsgruppen
*/
public function __construct(
private ?string $message = null,
private array $groups = []
) {
}
public function validate(mixed $value): bool
{
return $value !== null && $value !== '';
}
public function getErrorMessages(): array
{
return [$this->message ?? 'Dieser Wert ist erforderlich.'];
}
public function belongsToGroup(string $group): bool
{
return empty($this->groups) || in_array($group, $this->groups);
}
}
```
## Kombinieren von Regeln
Validierungsregeln können kombiniert werden, um komplexe Validierungsanforderungen zu erfüllen:
```php
class UserData
{
#[Required]
#[StringLength(min: 3, max: 50)]
#[Pattern(pattern: '/^[a-zA-Z0-9_]+$/')]
public string $username;
#[Required]
#[Email]
public string $email;
#[Required]
#[StringLength(min: 8)]
#[Custom(callback: 'validatePassword')]
public string $password;
private function validatePassword($value): bool
{
// Mindestens ein Großbuchstabe, ein Kleinbuchstabe, eine Zahl und ein Sonderzeichen
return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).+$/', $value) === 1;
}
}
```
## Weiterführende Informationen
- [Validation Framework Übersicht](index.md)
- [Beispiele für die Verwendung von Validierungsregeln](examples.md)
- [API-Validierung](/guides/api-validation.md)

View File

@@ -0,0 +1,279 @@
# WAF Konfiguration
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der WAF-Konfiguration korrekt dar.
## Übersicht
Die Web Application Firewall (WAF) bietet umfangreiche Konfigurationsmöglichkeiten, um das Sicherheitsniveau und die Leistung an die Anforderungen Ihrer Anwendung anzupassen. Diese Dokumentation beschreibt die verfügbaren Konfigurationsoptionen und wie Sie diese anwenden können.
## Konfigurationsklasse
Die zentrale Klasse für die WAF-Konfiguration ist `WafConfig`. Diese Klasse enthält alle Einstellungen, die das Verhalten der WAF steuern:
```php
// Konfiguration erstellen
$config = new WafConfig(
$enabled,
$defaultLayerConfig,
$globalTimeout,
$blockingThreshold,
$alertThreshold,
$parallelProcessing,
$maxParallelLayers,
$detailedLogging,
$metricsEnabled,
$enabledLayers,
$layerConfigs,
$customSettings
);
```
## Vordefinierte Konfigurationen
Die `WafConfig`-Klasse bietet mehrere vordefinierte Konfigurationen für verschiedene Umgebungen:
### Produktionsumgebung
```php
// Produktionskonfiguration verwenden
$config = WafConfig::production();
```
Die Produktionskonfiguration aktiviert alle Sicherheitsfeatures mit strengen Einstellungen:
- Alle Schutzebenen aktiviert
- Niedrige Schwellenwerte für Blockierung
- Parallele Verarbeitung für optimale Leistung
- Detaillierte Metriken für Überwachung
### Entwicklungsumgebung
```php
// Entwicklungskonfiguration verwenden
$config = WafConfig::development();
```
Die Entwicklungskonfiguration ist weniger streng und bietet:
- Höhere Schwellenwerte für Blockierung
- Detailliertes Logging für Debugging
- Alle Schutzebenen aktiviert, aber mit weniger strengen Einstellungen
### Testumgebung
```php
// Testkonfiguration verwenden
$config = WafConfig::testing();
```
Die Testkonfiguration ist für automatisierte Tests optimiert:
- Reduzierte Timeouts
- Deaktivierte parallele Verarbeitung
- Minimales Logging
### Deaktivierte Konfiguration
```php
// WAF deaktivieren
$config = WafConfig::disabled();
```
Diese Konfiguration deaktiviert die WAF vollständig, was für bestimmte Entwicklungs- oder Diagnoseszenarien nützlich sein kann.
## Hauptkonfigurationsoptionen
### Aktivierung/Deaktivierung
```php
// WAF aktivieren
$config = $config->enable();
// WAF deaktivieren
$config = $config->disable();
```
### Timeout-Einstellungen
```php
// Globales Timeout für WAF-Verarbeitung setzen
$config = $config->withGlobalTimeout(Duration::fromMilliseconds(100));
```
### Schwellenwerte
```php
// Schwellenwert für Blockierung setzen
$config = $config->withBlockingThreshold(Percentage::fromFloat(0.75));
```
Der Blockierungsschwellenwert bestimmt, ab welchem Konfidenzwert ein Request blockiert wird. Ein höherer Wert bedeutet weniger Blockierungen (und potenziell mehr falsch negative Ergebnisse), während ein niedrigerer Wert mehr Blockierungen (und potenziell mehr falsch positive Ergebnisse) bedeutet.
## Schutzebenen-Konfiguration
Die WAF verwendet ein mehrschichtiges Schutzsystem. Jede Schutzebene kann individuell konfiguriert werden:
```php
// Schutzebene aktivieren mit benutzerdefinierter Konfiguration
$config = $config->enableLayer(
'statistical',
new LayerConfig(
$enabled = true,
$weight = 0.6,
$threshold = Percentage::fromFloat(0.7),
$timeout = Duration::fromMilliseconds(50)
)
);
// Schutzebene deaktivieren
$config = $config->disableLayer('clustering');
```
### Verfügbare Schutzebenen
- **statistical**: Statistische Anomalieerkennung
- **clustering**: Clustering-basierte Anomalieerkennung
- **signature**: Signaturbasierte Erkennung bekannter Angriffsmuster
- **behavioral**: Verhaltensbasierte Anomalieerkennung
- **ratelimiting**: Ratenbegrenzung für Anfragen
## Benutzerdefinierte Einstellungen
Die WAF unterstützt benutzerdefinierte Einstellungen für spezielle Anwendungsfälle:
```php
// Benutzerdefinierte Einstellung hinzufügen
$config = $config->withCustomSetting('whitelist_ips', ['127.0.0.1', '192.168.1.1']);
// Benutzerdefinierte Einstellung abrufen
$whitelistIps = $config->getCustomSetting('whitelist_ips', []);
```
## Konfigurationsvalidierung
Die WAF-Konfiguration kann validiert werden, um sicherzustellen, dass alle Einstellungen gültig sind:
```php
// Konfiguration validieren
$validationErrors = $config->validate();
// Prüfen, ob die Konfiguration gültig ist
if (!$config->isValid()) {
// Fehlerbehandlung
}
```
## Konfiguration in der Anwendung
### Konfigurationsdatei
Die WAF-Konfiguration kann in einer dedizierten Konfigurationsdatei definiert werden:
```php
// config/waf.php
return [
'enabled' => true,
'learning_mode' => false,
'threshold' => 0.75,
'detectors' => [
'statistical' => true,
'clustering' => true
],
'whitelist' => [
'ips' => ['127.0.0.1'],
'paths' => ['/api/health']
]
];
```
### Laufzeitkonfiguration
Die WAF-Konfiguration kann zur Laufzeit aktualisiert werden:
```php
// WAF-Konfiguration aktualisieren
$waf->updateConfig($newConfig);
```
## Leistungsoptimierung
### Parallele Verarbeitung
Die parallele Verarbeitung kann die Leistung der WAF verbessern, insbesondere bei mehreren aktivierten Schutzebenen:
```php
// Parallele Verarbeitung aktivieren
$config = new WafConfig(
$enabled = true,
$defaultLayerConfig,
$globalTimeout,
$blockingThreshold,
$alertThreshold,
$parallelProcessing = true,
$maxParallelLayers = 4,
$detailedLogging,
$metricsEnabled,
$enabledLayers,
$layerConfigs,
$customSettings
);
```
### Caching
Die WAF unterstützt Caching für wiederholte Anfragen, um die Leistung zu verbessern:
```php
// Benutzerdefinierte Caching-Einstellungen
$config = $config->withCustomSetting('cache_ttl', 300); // 5 Minuten
$config = $config->withCustomSetting('cache_size', 1000); // 1000 Einträge
```
## Fehlerbehebung
### Logging
Detailliertes Logging kann für die Fehlerbehebung aktiviert werden:
```php
// Detailliertes Logging aktivieren
$config = new WafConfig(
$enabled = true,
$defaultLayerConfig,
$globalTimeout,
$blockingThreshold,
$alertThreshold,
$parallelProcessing,
$maxParallelLayers,
$detailedLogging = true,
$metricsEnabled,
$enabledLayers,
$layerConfigs,
$customSettings
);
```
### Metriken
Die WAF kann Metriken zur Leistung und Effektivität sammeln:
```php
// Metriken aktivieren
$config = new WafConfig(
$enabled = true,
$defaultLayerConfig,
$globalTimeout,
$blockingThreshold,
$alertThreshold,
$parallelProcessing,
$maxParallelLayers,
$detailedLogging,
$metricsEnabled = true,
$enabledLayers,
$layerConfigs,
$customSettings
);
```
## Weiterführende Informationen
- [WAF-Übersicht](index.md)
- [Machine Learning Dokumentation](machine-learning.md)
- [Sicherheitsrichtlinien](/guides/security.md)

View File

@@ -0,0 +1,280 @@
# WAF Feedback System
The Web Application Firewall (WAF) Feedback System allows users to provide feedback on WAF detections, helping to improve the accuracy of the security system over time. This document explains how to use the feedback system, how it works, and how to integrate it into your application.
## Table of Contents
1. [User Guide](#user-guide)
- [Submitting Feedback](#submitting-feedback)
- [Types of Feedback](#types-of-feedback)
- [Feedback Dashboard](#feedback-dashboard)
2. [Technical Documentation](#technical-documentation)
- [Architecture](#architecture)
- [Components](#components)
- [Integration](#integration)
3. [Learning Algorithms](#learning-algorithms)
- [False Positive Reduction](#false-positive-reduction)
- [False Negative Reduction](#false-negative-reduction)
- [Severity Adjustment](#severity-adjustment)
4. [Examples](#examples)
- [API Examples](#api-examples)
- [Dashboard Examples](#dashboard-examples)
- [Learning Examples](#learning-examples)
## User Guide
### Submitting Feedback
When the WAF detects a potential security threat, it may block the request or display a warning. Users with appropriate permissions can provide feedback on these detections to help improve the system's accuracy.
To submit feedback:
1. Navigate to the security event in the admin dashboard
2. Click on the "Provide Feedback" button
3. Select the type of feedback (false positive, false negative, correct detection, or severity adjustment)
4. Add an optional comment explaining your feedback
5. Click "Submit"
Alternatively, you can use the API to submit feedback programmatically (see [API Examples](#api-examples)).
### Types of Feedback
The WAF Feedback System supports four types of feedback:
1. **False Positive**: The WAF incorrectly identified a legitimate request as a security threat. This feedback helps reduce false alarms.
2. **False Negative**: The WAF failed to detect a security threat. This feedback helps improve detection coverage.
3. **Correct Detection**: The WAF correctly identified a security threat. This feedback confirms the accuracy of the detection.
4. **Severity Adjustment**: The WAF correctly identified a security threat, but the severity level was incorrect. This feedback helps calibrate the severity assessment.
### Feedback Dashboard
The Feedback Dashboard provides an overview of all feedback submitted to the WAF system, along with analytics and trends. To access the dashboard:
1. Navigate to Admin > Security > WAF > Feedback
2. Use the filters to view specific types of feedback, categories, or time periods
3. View charts and graphs showing feedback trends and detection accuracy
4. Access detailed reports for specific detection categories
## Technical Documentation
### Architecture
The WAF Feedback System consists of several components that work together to collect, store, analyze, and learn from user feedback:
```
+-------------------+ +-------------------+ +-------------------+
| | | | | |
| Feedback API | --> | Feedback Service | --> | Feedback Storage |
| | | | | |
+-------------------+ +-------------------+ +-------------------+
|
v
+-------------------+ +-------------------+ +-------------------+
| | | | | |
| Learning Service | <-- | Analytics | --> | Reporting |
| | | | | |
+-------------------+ +-------------------+ +-------------------+
|
v
+-------------------+
| |
| ML Engine |
| |
+-------------------+
```
### Components
The WAF Feedback System includes the following components:
1. **Feedback API**: Provides endpoints for submitting and retrieving feedback.
- `POST /api/security/waf/feedback`: Submit feedback
- `GET /api/security/waf/feedback/{detectionId}`: Get feedback for a specific detection
- `GET /api/security/waf/feedback/stats`: Get feedback statistics
- `GET /api/security/waf/feedback/recent`: Get recent feedback
2. **Feedback Service**: Handles the business logic for feedback submission and retrieval.
- `FeedbackService`: Core service for handling feedback
- `DetectionFeedback`: Value object representing feedback
- `FeedbackType`: Enum of feedback types
3. **Feedback Storage**: Stores feedback data for later analysis.
- `FeedbackRepositoryInterface`: Interface for feedback storage
- `DatabaseFeedbackRepository`: Database implementation
4. **Learning Service**: Analyzes feedback and adjusts the WAF models.
- `FeedbackLearningService`: Core service for learning from feedback
- `ModelAdjustment`: Value object representing model adjustments
5. **Analytics**: Analyzes feedback data to identify patterns and trends.
- `FeedbackReportGenerator`: Generates reports from feedback data
6. **Reporting**: Provides visualizations and reports on feedback data.
- `WafFeedbackDashboardController`: Controller for the feedback dashboard
7. **ML Engine**: Machine learning engine that uses feedback to improve detection.
- `MachineLearningEngine`: Core ML engine
- `ThresholdAdjustableInterface`: Interface for adjustable thresholds
- `ConfidenceAdjustableInterface`: Interface for adjustable confidence
- `FeatureWeightAdjustableInterface`: Interface for adjustable feature weights
### Integration
To integrate the WAF Feedback System into your application:
1. **Register Services**: Register the feedback services in your dependency injection container:
```php
// Register feedback services
$container->singleton(FeedbackRepositoryInterface::class, DatabaseFeedbackRepository::class);
$container->singleton(FeedbackService::class, FeedbackService::class);
$container->singleton(FeedbackLearningService::class, FeedbackLearningService::class);
$container->singleton(FeedbackReportGenerator::class, FeedbackReportGenerator::class);
```
2. **Register Controllers**: Register the feedback controllers:
```php
// Register feedback controllers
$container->singleton(WafFeedbackController::class, WafFeedbackController::class);
$container->singleton(WafFeedbackDashboardController::class, WafFeedbackDashboardController::class);
```
3. **Schedule Learning**: Schedule the learning process to run periodically:
```php
// Schedule learning process to run daily
$scheduler->daily('waf:learn-from-feedback');
```
4. **Add UI Components**: Add feedback UI components to your application:
```php
// Add feedback button to WAF detection alerts
$alertComponent->addAction('Provide Feedback', '/admin/security/waf/feedback/submit/{detectionId}');
```
## Learning Algorithms
The WAF Feedback System uses several learning algorithms to improve detection accuracy based on user feedback.
### False Positive Reduction
When users report false positives, the system adjusts the detection models to reduce the likelihood of similar false positives in the future:
1. **Threshold Adjustment**: Increases the detection threshold for the affected category, making it less sensitive.
2. **Confidence Reduction**: Decreases the confidence score for similar detections, making them less likely to trigger alerts.
3. **Feature Weight Adjustment**: Reduces the weights of features that contributed to the false positive.
### False Negative Reduction
When users report false negatives, the system adjusts the detection models to improve detection coverage:
1. **Threshold Adjustment**: Decreases the detection threshold for the affected category, making it more sensitive.
2. **Confidence Increase**: Increases the confidence score for similar detections, making them more likely to trigger alerts.
3. **Feature Weight Adjustment**: Increases the weights of features that should have contributed to detection.
### Severity Adjustment
When users report incorrect severity levels, the system adjusts the severity assessment:
1. **Confidence Adjustment**: Adjusts the confidence score based on the severity change.
2. **Category Default Severity**: Updates the default severity for the detection category.
## Examples
### API Examples
#### Submitting Feedback
```javascript
// Example: Submit false positive feedback
fetch('/api/security/waf/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({
detection_id: 'abc123',
feedback_type: 'false_positive',
category: 'SQL_INJECTION',
severity: 'HIGH',
comment: 'This is a legitimate query used by our reporting system',
context: {
query: 'SELECT * FROM reports WHERE date > ?'
}
})
})
.then(response => response.json())
.then(data => console.log(data));
```
#### Getting Feedback Statistics
```javascript
// Example: Get feedback statistics
fetch('/api/security/waf/feedback/stats')
.then(response => response.json())
.then(data => {
console.log('Total feedback:', data.stats.total_count);
console.log('False positives:', data.stats.by_feedback_type.false_positive);
console.log('False negatives:', data.stats.by_feedback_type.false_negative);
});
```
### Dashboard Examples
#### Viewing Feedback Trends
The Feedback Dashboard provides visualizations of feedback trends over time:
- **Accuracy Trend**: Shows how detection accuracy has improved over time
- **False Positive Rate**: Shows how the false positive rate has decreased
- **False Negative Rate**: Shows how the false negative rate has decreased
- **Category Breakdown**: Shows feedback distribution by detection category
#### Analyzing Detection Categories
The Category Analysis view shows detailed information about each detection category:
- **Accuracy**: Detection accuracy for the category
- **False Positive Rate**: False positive rate for the category
- **False Negative Rate**: False negative rate for the category
- **Severity Distribution**: Distribution of severity levels for the category
- **Common Patterns**: Common patterns in false positives and false negatives
### Learning Examples
#### Scheduled Learning
The learning process can be scheduled to run periodically:
```bash
# Run learning process daily
php console.php waf:learn-from-feedback
```
#### Manual Learning
The learning process can also be triggered manually:
```bash
# Run learning process with custom parameters
php console.php waf:learn-from-feedback --threshold=10 --learning-rate=0.5
```
#### Learning Results
The learning process provides detailed results:
```
Starting WAF feedback learning process...
Processing 25 false positives, 10 false negatives, 5 severity adjustments
Created 3 model adjustments for SQL_INJECTION, XSS, COMMAND_INJECTION
Applied 3 model adjustments
Learning process completed successfully in 0.5 seconds
```

View File

@@ -0,0 +1,135 @@
# WAF (Web Application Firewall)
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der WAF-Komponente korrekt dar.
## Übersicht
Die WAF-Komponente (Web Application Firewall) ist ein fortschrittliches Sicherheitssystem, das Webanwendungen vor verschiedenen Bedrohungen schützt. Es verwendet Machine Learning und statistische Analysen, um Anomalien zu erkennen und potenzielle Angriffe zu blockieren, bevor sie Schaden anrichten können.
## Hauptkomponenten
### WAF-Klasse
Die zentrale Klasse für die WAF-Funktionalität:
```php
// Klasse initialisieren
$waf = new Waf($config, $eventDispatcher);
// Request prüfen
$result = $waf->analyzeRequest($request);
// Entscheidung treffen
if ($result->isBlocked()) {
// Request blockieren
}
```
### MachineLearningEngine
Verarbeitet Requests mit Machine-Learning-Algorithmen:
- Extrahiert Features aus Requests
- Vergleicht mit bekannten Mustern
- Erkennt Anomalien und ungewöhnliches Verhalten
- Klassifiziert potenzielle Bedrohungen
### Detektoren
Verschiedene Detektoren für unterschiedliche Arten von Anomalien:
- `StatisticalAnomalyDetector`: Erkennt statistische Abweichungen
- `ClusteringAnomalyDetector`: Gruppiert ähnliche Requests und identifiziert Ausreißer
- Erweiterbar für zusätzliche Detektoren
### Middleware-System
```php
// In Bootstrap oder Application-Klasse
$app->addMiddleware(WafMiddleware::class);
```
## Feature-Extraktion
Die WAF extrahiert verschiedene Features aus eingehenden Requests:
1. **Request-Metadaten**: URL, HTTP-Methode, Header
2. **Parameter-Analyse**: Anzahl, Länge, Entropie der Parameter
3. **Inhaltsmuster**: Spezielle Zeichen, Skript-Tags, SQL-Fragmente
4. **Verhaltensanalyse**: Anfragehäufigkeit, Sitzungsdauer, Navigationsmuster
## Anomalie-Erkennung
Der Anomalie-Erkennungsprozess umfasst:
1. **Feature-Extraktion** aus dem eingehenden Request
2. **Baseline-Vergleich** mit normalen Verhaltensmustern
3. **Anomalie-Bewertung** durch verschiedene Detektoren
4. **Entscheidungsfindung** basierend auf Schwellenwerten und Regeln
## Konfiguration
Die WAF kann über die Konfigurationsdatei angepasst werden:
```php
// config/waf.php
return [
'enabled' => true,
'learning_mode' => false,
'threshold' => 0.75,
'detectors' => [
'statistical' => true,
'clustering' => true
],
'whitelist' => [
'ips' => ['127.0.0.1'],
'paths' => ['/api/health']
]
];
```
## Integration
### Service-Container
Die WAF wird automatisch registriert und kann per Dependency Injection verwendet werden:
```php
public function __construct(private readonly Waf $waf) {}
```
### Event-Integration
Die WAF löst folgende Events aus:
- `waf.request_analyzed`: Nach der Analyse eines Requests
- `waf.request_blocked`: Wenn ein Request blockiert wird
- `waf.anomaly_detected`: Bei Erkennung einer Anomalie
## Feedback-System (geplant)
Ein Feedback-System für die WAF ist in Planung und wird folgende Funktionen bieten:
- Admin-Interface zur Überprüfung von WAF-Entscheidungen
- Feedback-Mechanismus für falsch positive/negative Ergebnisse
- Kontinuierliches Training der ML-Modelle basierend auf Feedback
## Fehlerbehebung
### Häufige Probleme
1. **Falsch positive Blockierungen**:
- Überprüfen Sie die Whitelist-Konfiguration
- Passen Sie den Schwellenwert an
- Aktivieren Sie den Lernmodus vorübergehend
2. **Leistungsprobleme**:
- Deaktivieren Sie rechenintensive Detektoren
- Implementieren Sie Caching für wiederholte Anfragen
- Optimieren Sie die Feature-Extraktion
## Weiterführende Informationen
- [Machine Learning Dokumentation](machine-learning.md)
- [WAF-Konfiguration](configuration.md)
- [Sicherheitsrichtlinien](/guides/security.md)

View File

@@ -0,0 +1,252 @@
# WAF Machine Learning
> **Dokumentationshinweis:** Diese Dokumentation ist vollständig aktualisiert und stellt die aktuelle Implementierung der Machine-Learning-Komponente der WAF korrekt dar.
## Übersicht
Die Machine-Learning-Komponente ist das Herzstück des WAF-Systems und ermöglicht die intelligente Erkennung von Bedrohungen und Anomalien in Echtzeit. Sie verwendet fortschrittliche Algorithmen, um normales Verhalten zu lernen und Abweichungen zu identifizieren, die auf potenzielle Angriffe hindeuten könnten.
## MachineLearningEngine
Die `MachineLearningEngine`-Klasse ist die zentrale Komponente des Machine-Learning-Systems:
```php
// Initialisierung
$engine = new MachineLearningEngine(
$enabled,
$extractors,
$detectors,
$clock,
$analysisTimeout,
$confidenceThreshold,
$enableParallelProcessing,
$enableFeatureCaching,
$maxFeaturesPerRequest
);
// Analyse eines Requests
$result = $engine->analyzeRequest($requestData, $context);
```
### Hauptfunktionen
- **Request-Analyse**: Analysiert eingehende Requests auf Anomalien
- **Feature-Extraktion**: Extrahiert relevante Features aus Requests
- **Anomalie-Erkennung**: Identifiziert Abweichungen vom normalen Verhalten
- **Baseline-Management**: Verwaltet und aktualisiert Verhaltensbaselines
- **Modell-Aktualisierung**: Passt Modelle kontinuierlich an neue Daten an
- **Leistungsüberwachung**: Überwacht und optimiert die Leistung des Systems
## Prozessablauf
Der Machine-Learning-Prozess umfasst folgende Schritte:
1. **Feature-Extraktion** (`extractFeatures`):
- Extrahiert relevante Features aus dem Request
- Validiert und normalisiert Features
- Wendet Feature-Caching an, wenn aktiviert
2. **Baseline-Abruf** (`getBaselines`):
- Ruft relevante Baselines für die extrahierten Features ab
- Erstellt neue Baselines, wenn keine existieren
- Verwaltet Baseline-Cache für optimale Leistung
3. **Anomalie-Erkennung** (`detectAnomalies`):
- Wendet verschiedene Detektoren auf die Features an
- Vergleicht Features mit Baselines
- Berechnet Anomalie-Scores und Konfidenzwerte
4. **Konfidenzberechnung** (`calculateOverallConfidence`):
- Aggregiert Ergebnisse verschiedener Detektoren
- Berechnet Gesamtkonfidenz basierend auf gewichteten Scores
- Vergleicht mit Schwellenwert für Entscheidungsfindung
5. **Modell-Aktualisierung** (`updateModels`):
- Aktualisiert Modelle mit neuen Daten
- Passt Baselines an sich ändernde Muster an
- Optimiert Detektoren basierend auf Feedback
## Feature-Extraktion
Die Feature-Extraktion ist ein kritischer Schritt im Machine-Learning-Prozess:
```php
$features = $engine->extractFeatures($requestData, $context);
```
### Feature-Typen
- **Request-Features**: URL-Struktur, Parameter, Header
- **Inhalts-Features**: Payload-Struktur, Entropie, Zeichenverteilung
- **Verhaltens-Features**: Anfragemuster, Sitzungsverhalten, Timing
- **Kontext-Features**: IP-Reputation, Geolocation, Benutzeragent
### Feature-Validierung
Alle extrahierten Features werden validiert, um Qualität und Konsistenz zu gewährleisten:
- Überprüfung auf Vollständigkeit und Gültigkeit
- Normalisierung für konsistente Verarbeitung
- Filterung irrelevanter oder redundanter Features
## Anomalie-Detektoren
Das System verwendet verschiedene Detektoren für unterschiedliche Arten von Anomalien:
### StatisticalAnomalyDetector
Erkennt statistische Abweichungen in numerischen Features:
- Berechnet Z-Scores für numerische Features
- Identifiziert Ausreißer basierend auf statistischen Verteilungen
- Erkennt ungewöhnliche Wertekombinationen
### ClusteringAnomalyDetector
Gruppiert ähnliche Requests und identifiziert Ausreißer:
- Verwendet Clustering-Algorithmen zur Gruppierung ähnlicher Requests
- Berechnet Distanz zu Cluster-Zentren
- Identifiziert isolierte Punkte als potenzielle Anomalien
### Erweiterbarkeit
Das System ist erweiterbar für zusätzliche Detektoren:
```php
// Eigenen Detektor registrieren
$config->registerDetector(new CustomAnomalyDetector());
```
## Baselines
Baselines repräsentieren normales Verhalten und dienen als Referenz für die Anomalie-Erkennung:
### Baseline-Typen
- **Statistische Baselines**: Mittelwerte, Standardabweichungen, Verteilungen
- **Verhaltensbaselines**: Typische Anfragemuster und Sequenzen
- **Inhaltsbaselines**: Normale Payload-Strukturen und Charakteristiken
### Baseline-Management
Baselines werden kontinuierlich aktualisiert und optimiert:
- Automatische Erstellung für neue Feature-Kombinationen
- Regelmäßige Aktualisierung basierend auf neuen Daten
- Gewichtete Historisierung für Stabilität und Aktualität
## Leistungsoptimierung
Die Machine-Learning-Komponente enthält verschiedene Optimierungen:
### Feature-Caching
```php
// Konfiguration
$config->setEnableFeatureCaching(true);
```
Speichert extrahierte Features für ähnliche Requests, um Rechenaufwand zu reduzieren.
### Parallele Verarbeitung
```php
// Konfiguration
$config->setEnableParallelProcessing(true);
```
Verarbeitet Features und Detektoren parallel für verbesserte Leistung.
### Leistungsmetriken
Das System sammelt Leistungsmetriken zur Überwachung und Optimierung:
- Verarbeitungszeiten für verschiedene Phasen
- Feature- und Anomaliezahlen
- Cache-Trefferquoten und Speichernutzung
## Konfiguration
Die Machine-Learning-Komponente bietet umfangreiche Konfigurationsmöglichkeiten:
```php
// config/waf_ml.php
return [
'enabled' => true,
'confidence_threshold' => 0.75,
'analysis_timeout' => 100, // ms
'enable_parallel_processing' => true,
'enable_feature_caching' => true,
'max_features_per_request' => 50,
'detectors' => [
'statistical' => [
'enabled' => true,
'weight' => 0.6
],
'clustering' => [
'enabled' => true,
'weight' => 0.4
]
]
];
```
## Integration mit WAF
Die Machine-Learning-Komponente ist nahtlos in das WAF-System integriert:
```php
// In WAF-Klasse
public function analyzeRequest(Request $request): WafResult
{
$requestData = $this->analyzer->analyze($request);
$mlResult = $this->mlEngine->analyzeRequest($requestData, $this->context);
return new WafResult($requestData, $mlResult);
}
```
## Fehlerbehebung
### Häufige Probleme
1. **Hohe Falsch-Positiv-Rate**:
- Erhöhen Sie den Konfidenz-Schwellenwert
- Aktivieren Sie den Lernmodus für einen längeren Zeitraum
- Überprüfen Sie die Gewichtung der Detektoren
2. **Leistungsprobleme**:
- Aktivieren Sie Feature-Caching
- Reduzieren Sie die maximale Anzahl von Features pro Request
- Optimieren Sie die Detektorkonfiguration
3. **Fehlende Erkennung**:
- Senken Sie den Konfidenz-Schwellenwert
- Aktivieren Sie zusätzliche Detektoren
- Überprüfen Sie die Feature-Extraktion
## Weiterentwicklung
### Geplante Erweiterungen
1. **Feedback-System**:
- Integration von Administratorfeedback
- Automatische Anpassung basierend auf Feedback
- Kontinuierliches Lernen aus Fehlklassifikationen
2. **Erweiterte Detektoren**:
- Deep-Learning-basierte Anomalieerkennung
- Sequenzanalyse für komplexe Angriffsmuster
- Verhaltensbasierte Benutzerprofilierung
3. **Verbesserte Visualisierung**:
- Dashboard für Anomalietrends
- Feature-Wichtigkeitsanalyse
- Echtzeit-Monitoring von Bedrohungen
## Weiterführende Informationen
- [WAF-Übersicht](index.md)
- [WAF-Konfiguration](configuration.md)
- [Sicherheitsrichtlinien](/guides/security.md)