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:
703
docs/components/cryptography/examples.md
Normal file
703
docs/components/cryptography/examples.md
Normal 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.
|
||||
Reference in New Issue
Block a user