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