# Die Signatur einer Zustellung prüfen

Am Ende nimmt Ihr Empfänger nur Zustellungen an, die wirklich von der Plattform stammen, verarbeitet jede genau einmal und übersteht einen Wechsel des Secrets.

> **Typ:** anleitung
> **Für wen:** Wer den Empfänger für Webhooks programmiert.
> **Voraussetzung:** Ein angelegter Endpunkt und sein Signatur-Secret (`whsec_…`), wie es beim Anlegen oder nach **Secret rotieren** angezeigt wurde.
> **Ort:** Ihr eigener Code; das Secret erneuern Sie unter **Einstellungen › Organisation › Webhooks**.

## Schritte

1. **Lesen Sie den Rumpf als rohe Bytes**, bevor irgendetwas ihn als JSON deutet. Die Signatur gilt für genau die Bytes, die gesendet wurden; wer den Rumpf erst einliest und neu serialisiert, ändert womöglich Reihenfolge oder Abstände und bekommt eine andere Signatur.
2. **Rechnen Sie HMAC-SHA256 über diese Bytes** mit dem Secret als Schlüssel und kodieren Sie das Ergebnis base64url, so wie Node.js es mit `digest('base64url')` erzeugt.
3. **Vergleichen Sie mit `X-Signature` in konstanter Zeit** und nur bei gleicher Länge. Ein gewöhnlicher Zeichenvergleich bricht beim ersten Unterschied ab und verrät über die Laufzeit, wie viele Zeichen stimmen.
4. **Verzweigen Sie nach `X-Event-Type`.** Die Kopfzeile nennt den Ereignistyp, ohne dass Sie den Rumpf lesen müssen.
5. **Entdoppeln Sie nach `X-Delivery-Id`.** Merken Sie sich jede verarbeitete Nummer. Eine Wiederholung trägt dieselbe Nummer; ist sie bekannt, antworten Sie mit 2xx und tun sonst nichts.
6. **Erst jetzt verarbeiten — und schnell mit 2xx antworten.** Das Zeitlimit beträgt zehn Sekunden; lange Arbeit gehört hinter die Antwort.

So sieht die Prüfung in Node.js aus; es ist das Beispiel, das auch das Portal unter **Zustellungen auf Echtheit prüfen** zeigt:

```ts
import { createHmac, timingSafeEqual } from 'node:crypto';

// Über den ROHEN Rumpf signieren, nicht über neu serialisiertes JSON:
// jede erneute Serialisierung kann Reihenfolge oder Abstände ändern.
export function isFromSignage(rawBody: string, header: string, secret: string): boolean {
  const expected = createHmac('sha256', secret).update(rawBody).digest('base64url');
  const a = Buffer.from(expected);
  const b = Buffer.from(header);
  return a.length === b.length && timingSafeEqual(a, b);
}
```

Der Aufruf im Empfänger: `isFromSignage(rawBody, req.headers['x-signature'], process.env.WEBHOOK_SECRET)`. Kopfzeilennamen kommen je nach Server in Kleinbuchstaben an; die Plattform sendet `X-Signature`.

## Optionen

### Das Secret erneuern

Das Secret hat die Form `whsec_` plus mindestens 32 Zeichen aus `A–Z`, `a–z`, `0–9`, `_` und `-`. Es wird nur beim Anlegen und nach einer Rotation angezeigt; abrufen lässt es sich nicht. Ist es verloren oder soll es gewechselt werden:

1. Stellen Sie Ihren Empfänger so ein, dass er das neue Secret sofort nach der Anzeige übernehmen kann — etwa über eine Konfiguration, die ohne Neustart nachlädt.
2. Klicken Sie im Portal an dem Endpunkt auf **Secret rotieren** (ohne ein Paket mit Webhooks gesperrt), oder rufen Sie `POST /v1/webhooks/{id}/rotate-secret` auf — über die API bleibt das auch ohne Paket möglich.
3. Übernehmen Sie das neue Secret aus dem Kasten **Signatur-Secret — nur jetzt sichtbar** (**Secret kopieren**) beziehungsweise aus der Antwort `{"secret": "whsec_…"}`.

Ab dem nächsten Versand wird ausschließlich mit dem neuen Secret signiert — auch Wiederholungen älterer Zustellungen. Es gibt keine Übergangsphase, in der beide gelten. Bis Ihr Empfänger das neue Secret kennt, weist er echte Zustellungen ab; die Plattform wiederholt sie nur, wenn Sie in dieser Zeit mit 5xx statt 4xx antworten. Adresse, Ereignisse und Zustell-Historie des Endpunkts bleiben bei der Rotation erhalten.

## Ergebnis prüfen

- Eine echte Zustellung passiert die Prüfung, und im **Zustell-Log** steht sie als **Zugestellt** mit Ihrem Antwortcode.
- Eine Anfrage mit verändertem Rumpf oder ohne gültige `X-Signature` wird abgewiesen.
- Eine erneut zugestellte Nummer (**Erneut zustellen** im Zustell-Log) löst bei Ihnen keine zweite Verarbeitung aus.

## Wenn etwas nicht klappt

| Symptom | Bedeutung | Lösung |
|---|---|---|
| Signatur passt nie | Der Rumpf wurde vor der Prüfung verändert (neu serialisiert, Zeichensatz umgewandelt, Leerraum entfernt) | Rohe Bytes prüfen, bevor ein JSON-Parser oder ein Rahmenwerk den Rumpf anfasst |
| Signatur passt seit einem bestimmten Zeitpunkt nicht mehr | Das Secret wurde rotiert; Ihr Empfänger prüft noch mit dem alten | Neues Secret übernehmen; Zustellungen aus der Lücke im Zustell-Log **Erneut zustellen** |
| Signatur passt, Vergleich schlägt trotzdem fehl | Längenprüfung oder Kodierung stimmen nicht (hex statt base64url, Füllzeichen angehängt) | Genau die Form aus dem Beispiel verwenden |
| Dieselbe Zustellung wird zweimal verarbeitet | Entdopplung fehlt; eine Wiederholung oder ein manuelles Erneut-Zustellen trug dieselbe `X-Delivery-Id` | Nummern speichern und bekannte Nummern mit 2xx quittieren |

## Weiterführendes

- [Was gesendet wird und wie wiederholt wird](/de/webhooks/delivery-and-retries)
- [Einen Webhook-Endpunkt anlegen](/de/webhooks/create-endpoint)
- [Wenn keine Zustellung ankommt](/de/webhooks/troubleshooting)
