# 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.