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