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:
397
docs/components/cryptography/configuration.md
Normal file
397
docs/components/cryptography/configuration.md
Normal 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.
|
||||
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.
|
||||
365
docs/components/cryptography/index.md
Normal file
365
docs/components/cryptography/index.md
Normal 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
|
||||
728
docs/components/cryptography/security.md
Normal file
728
docs/components/cryptography/security.md
Normal 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.
|
||||
Reference in New Issue
Block a user