# Warum stimmt meine Webhook-Signatur nicht überein?

> Beheben Sie Fehler bei der Verifizierung von Webhook-Signaturen. Signieren Sie den unveränderten Body mit dem vollständigen whsec_-Secret und dem Wert von X-Emailit-Timestamp.

Dieser Artikel hilft, wenn Ihr Endpunkt Webhooks von Emailit ablehnt, weil die von Ihnen berechnete Signatur nicht dem Header `X-Emailit-Signature` entspricht. Fast immer unterscheiden sich die Bytes, die Sie signieren, oder das Secret, das Sie verwenden, von dem, was Emailit verwendet hat.

## Symptome

- Ihr Handler gibt für jeden Webhook `401` oder `400` mit „invalid signature“ zurück, auch für Events von **Send test** aus der Weboberfläche.
- Anfragen auf dem Tab **Requests** des Webhooks zeigen fehlgeschlagene Versuche mit dem Fehlerstatuscode Ihres Endpunkts.
- Die Verifizierung funktioniert in einem Unit-Test mit einem selbst erstellten Payload, schlägt aber bei echten Zustellungen fehl.

## Ursache

Emailit signiert jede Anfrage so:

```text
signature = hex( HMAC-SHA256( secret, "<X-Emailit-Timestamp>.<raw request body>" ) )
```

Die Signatur schlägt fehl, wenn sich eine der drei Eingaben unterscheidet:

- **Der Body wurde neu serialisiert.** Frameworks parsen JSON oft, bevor Ihr Handler läuft. `JSON.stringify(req.body)` erzeugt andere Leerzeichen und eine andere Reihenfolge der Schlüssel als die Bytes, die Emailit gesendet hat. Der Body ist ein JSON-**Array** mit bis zu 100 Events, daher kann auch Code, der ein Objekt erwartet, ihn umformen.
- **Das Secret ist falsch.** Verwenden Sie den vollständigen Wert einschließlich des Präfixes `whsec_`. Jeder Webhook hat ein eigenes Secret, und das Zurücksetzen macht das alte sofort ungültig, auch für ausstehende Wiederholungen.
- **Der Zeitstempel fehlt oder weicht ab.** Verwenden Sie den genauen Wert des Headers `X-Emailit-Timestamp` und verbinden Sie ihn mit einem einzelnen Punkt mit dem Body.
- **Der Vergleich ist falsch.** Der Header ist hexadezimal in Kleinbuchstaben kodiert, nicht Base64, und hat kein Präfix `sha256=`.
- **Etwas vor Ihrer App hat den Body verändert**, etwa ein Proxy, der ihn neu kodiert oder dekomprimiert.

## Lösung

1. **Unveränderten Body lesen.** Erfassen Sie die Bytes, bevor ein JSON-Parser läuft. Binden Sie in Express `express.raw()` nur für die Webhook-Route ein:

```javascript title="webhook.js"
import crypto from 'node:crypto';

app.post('/webhooks/emailit', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Emailit-Timestamp');
  const expected = crypto
    .createHmac('sha256', process.env.EMAILIT_WEBHOOK_SECRET) // whsec_...
    .update(`${timestamp}.${req.body.toString('utf8')}`)
    .digest('hex');
  const received = req.get('X-Emailit-Signature') ?? '';
  const valid = received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  if (!valid) return res.sendStatus(401);

  const events = JSON.parse(req.body); // an array of events
  res.sendStatus(200);
});
```

   In Route-Handlern von Next.js verwenden Sie `await request.text()`, in Laravel `$request->getContent()` und in Flask `request.get_data()`.

2. **Richtiges Secret beschaffen.** Rufen Sie [Webhook abrufen](/de/docs/api-reference/webhooks/get/) auf, das das aktuelle `secret` zurückgibt. In der Weboberfläche wird das Secret nur einmal angezeigt. Wenn Sie es verloren haben, öffnen Sie den Webhook, setzen Sie es mit **Webhook secret** zurück und aktualisieren Sie sofort Ihre Umgebungsvariable.

3. **Test-Event senden.** Wählen Sie auf der Seite des Webhooks **Send test** und einen Event-Typ. Der Test wird mit demselben Secret signiert, und der Dialog zeigt den Statuscode und den Antwort-Body Ihres Endpunkts.

4. **Toleranz für die Uhrzeit prüfen.** Wenn Sie alte Zeitstempel ablehnen, um Replay-Angriffe zu verhindern, lassen Sie einige Minuten Spielraum und halten Sie die Uhr Ihres Servers synchron. Jeder Zustellversuch, auch jede Wiederholung, trägt einen neuen Zeitstempel.

5. **Fehlgeschlagenes wiederholen.** Sobald die Verifizierung funktioniert, wählen Sie **Retry failed**, um fehlgeschlagene Zustellungen der letzten 7 Tage erneut zu senden.

Code in anderen Sprachen finden Sie unter [Signatur von Webhook-Anfragen](/de/docs/webhooks/request-signature/), das Format des Bodys unter [Webhook-Anfragen](/de/docs/webhooks/webhook-requests/).

## Weiterhin Probleme?

[Kontaktieren Sie den Support](/contact/) oder fragen Sie auf [Discord](https://discord.emailit.com). Teilen Sie die Webhook-ID (`wh_…`) und die ID eines fehlschlagenden Events mit, nie das Secret.

---
Quelle: https://emailit.com/de/docs/kb/webhook-signature-mismatch/
