# Webhooks einfach erklärt: Signatur, Wiederholung, Sicherheit

> Was ein Webhook ist, wie er sich von der API-Abfrage unterscheidet und wie Sie Signatur, Wiederholung und Dubletten behandeln. Mit curl und Node.

Quelle: https://whovisit.app/blog/webhooks-einfach-erklaert
Veröffentlicht: 2026-11-07
Herausgeber: Whovisit (ARAVILO, Innsbruck)

Ein Webhook ist ein Anruf in die andere Richtung. Statt dass Ihr System bei einem Dienst alle paar Minuten nachfragt, ob etwas passiert ist, ruft der Dienst eine Adresse bei Ihnen auf, sobald es passiert. Das spart Abfragen und liefert Neuigkeiten schneller. Dafür müssen Sie einen Empfänger bereitstellen und absichern. Dieser Artikel erklärt beides.

## Webhook oder API-Abfrage

| | API-Abfrage (Polling) | Webhook |
|---|---|---|
| Wer startet? | Ihr System | Der Absender |
| Wann? | Nach Zeitplan, etwa stündlich | Sobald ein Ereignis eintritt |
| Aufwand | Anfragen auch ohne Neuigkeiten | Ein erreichbarer Empfänger nötig |
| Gut für | Berichte, Abgleich, Nachladen | Benachrichtigung, Weiterleitung |
| Risiko | Verzögerung bis zur nächsten Abfrage | Nachricht geht verloren, wenn der Empfänger nicht antwortet |

In der Praxis kombinieren viele beides: Der Webhook meldet das Ereignis, die API holt bei Bedarf die Details oder schließt Lücken nach einem Ausfall.

## Wie eine Zustellung abläuft

1. Ein Ereignis tritt ein, etwa „Zahlung eingegangen“.
2. Der Absender schickt einen POST mit JSON an Ihre hinterlegte Adresse (HTTPS).
3. Ihr Empfänger prüft die Signatur, merkt sich die Nachricht und antwortet schnell mit einem Code aus dem 2xx-Bereich.
4. Die eigentliche Arbeit erledigt Ihr System danach.

Schritt 3 ist wichtig. Stripe empfiehlt in der [Webhook-Dokumentation](https://docs.stripe.com/webhooks), sofort einen 2xx-Code zurückzugeben, bevor aufwendige Logik läuft, die einen Timeout auslösen könnte. Wer erst die Rechnung bucht und dann antwortet, riskiert unnötige Wiederholungen.

## Signatur: wie Sie die Herkunft prüfen

Ihre Adresse ist öffentlich erreichbar. Jeder könnte also erfundene Nachrichten an sie schicken. Deshalb signieren seriöse Absender jede Nachricht mit einem gemeinsamen Geheimnis. Üblich ist ein HMAC mit SHA-256 über den Inhalt, oft zusammen mit einem Zeitstempel. [GitHub beschreibt das Verfahren](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries) so: HMAC-SHA256 über den Inhalt, hexadezimal dargestellt, mit der Kopfzeile `X-Hub-Signature-256`. Der Vergleich soll mit einer Funktion mit konstanter Laufzeit erfolgen, damit Angreifer aus Antwortzeiten nichts ableiten können.

Wichtige Punkte für Ihren Empfänger:

- **Rohinhalt verwenden.** Berechnen Sie die Signatur über die empfangenen Bytes, nicht über das erneut serialisierte JSON. Schon ein anderer Zeilenumbruch ändert die Signatur.
- **Zeitstempel prüfen.** Stripe verwendet einen Zeitstempel in der Signatur und eine Standardtoleranz von fünf Minuten, um Wiederholungsangriffe zu verhindern. Eine Toleranz von 0 schaltet die Prüfung ab.
- **Geheimnis geheim halten.** Nicht im Code, nicht im Repository, sondern in einer Umgebungsvariable oder einem Tresor.

Open-Source-Richtlinien für Absender und Empfänger bündelt die Initiative [Standard Webhooks](https://www.standardwebhooks.com/). Wie jeder Anbieter seine Signatur konkret bildet, steht in dessen Dokumentation.

## Wiederholungen und Doppelzustellung

Antwortet Ihr Empfänger nicht oder mit einem Fehlercode, versucht der Absender es erneut. Stripe etwa wiederholt nach der eigenen Dokumentation mit steigenden Abständen bis zu drei Tage lang. Andere Anbieter haben andere Regeln, manche schalten den Webhook nach zu vielen Fehlschlägen ab. Daraus folgt:

- **Rechnen Sie mit Dubletten.** Dasselbe Ereignis kann mehrfach ankommen, auch wenn Ihr Empfänger korrekt arbeitet. Stripe weist darauf ausdrücklich hin.
- **Machen Sie die Verarbeitung idempotent.** Das heißt: Wer dieselbe Nachricht zweimal verarbeitet, erzeugt kein zweites Ergebnis. Speichern Sie die Kennung des Ereignisses und überspringen Sie Bekanntes.
- **Verlassen Sie sich nicht auf die Reihenfolge.** Ereignisse können vertauscht eintreffen. Prüfen Sie im Zweifel den aktuellen Zustand per API.

## Sicherheit auf beiden Seiten

**Für Empfänger:** nur HTTPS, Signatur und Alter prüfen, Größe der Nachricht begrenzen, keine Befehle aus dem Inhalt ausführen. Behandeln Sie den Inhalt wie jede Eingabe von außen.

**Für Absender, die Adressen von Kunden aufrufen:** Wer Webhooks anbietet, ruft Adressen auf, die andere eingetragen haben. Das ist ein klassischer Weg für Server-Side Request Forgery. Der Angreifer trägt eine interne Adresse ein. Der Server ruft sie aus dem geschützten Netz heraus auf. Die [OWASP-Beschreibung](https://community.owasp.org/attacks/Server_Side_Request_Forgery) nennt als Ziele zum Beispiel interne Dienste und Metadatenadressen von Cloudanbietern. Sinnvolle Schutzmaßnahmen:

- nur `https://` zulassen,
- die Adresse beim Eintragen und vor jedem Aufruf auflösen und private, lokale und reservierte Bereiche ablehnen,
- Weiterleitungen nicht blind folgen,
- kurze Zeitlimits setzen.

## Beispiel: Testnachricht mit curl senden

Mit curl und openssl können Sie Ihren Empfänger ohne den echten Absender testen. Das Skript sendet eine signierte Nachricht, wie sie ein Absender mit Zeitstempel schicken würde:

```bash
GEHEIMNIS="test-geheimnis"
ZEIT=$(date +%s)
INHALT='{"id":"evt_1","typ":"besuch.begonnen","firma":"beispiel-firma.example"}'
SIGNATUR="sha256=$(printf '%s.%s' "$ZEIT" "$INHALT" | openssl dgst -sha256 -hmac "$GEHEIMNIS" | sed 's/^.* //')"

curl -X POST http://localhost:3000/webhook \
  -H "Content-Type: application/json" \
  -H "X-Zeit: $ZEIT" \
  -H "X-Signatur: $SIGNATUR" \
  -d "$INHALT"
```

## Beispiel: ein kleiner Empfänger in Node.js

```js
const http = require('http')
const crypto = require('crypto')
const GEHEIMNIS = process.env.WEBHOOK_GEHEIMNIS || 'test-geheimnis'
const gesehen = new Set() // in echt: Datenbank

http.createServer((req, res) => {
  if (req.method !== 'POST' || req.url !== '/webhook') { res.writeHead(404); return res.end() }
  const teile = []
  req.on('data', t => teile.push(t))
  req.on('end', () => {
    const roh = Buffer.concat(teile).toString('utf8')
    const zeit = req.headers['x-zeit'] || ''
    const erwartet = 'sha256=' + crypto.createHmac('sha256', GEHEIMNIS).update(zeit + '.' + roh).digest('hex')
    const erhalten = String(req.headers['x-signatur'] || '')
    const a = Buffer.from(erwartet), b = Buffer.from(erhalten)
    const echt = a.length === b.length && crypto.timingSafeEqual(a, b)
    const frisch = Math.abs(Date.now() / 1000 - Number(zeit)) < 300
    if (!echt || !frisch) { res.writeHead(401); return res.end() }
    const ereignis = JSON.parse(roh)
    if (!gesehen.has(ereignis.id)) { gesehen.add(ereignis.id); console.log('neu:', ereignis.typ) }
    res.writeHead(200); res.end('ok')
  })
}).listen(3000)
```

Der Empfänger prüft Signatur und Alter, überspringt bekannte Kennungen und antwortet sofort. Für den Betrieb brauchen Sie zusätzlich HTTPS, eine Größenbegrenzung und eine Warteschlange für die Weiterverarbeitung.

## Wo Webhooks verbreitet sind

Zahlungsdienste, Code-Plattformen und Formulartools melden Ereignisse per Webhook. Auch Besuchererkennung kann es: Meldet das System eine Firma, die Ihre Seite besucht, landet sie direkt in einem Tool Ihrer Wahl. Wie das in ein Vertriebssystem führt, zeigt [Website-Besuche ins CRM bringen](https://whovisit.app/blog/website-besuche-ins-crm). Die Alternative per Abfrage beschreibt [Welche Firmen besuchen meine Website?](https://whovisit.app/blog/welche-firmen-besuchen-meine-website). Welche Daten dabei fließen, erklärt [Wie funktioniert Firmenerkennung?](https://whovisit.app/blog/firmenerkennung-website-technik).

## Häufige Fragen

**Was ist der Unterschied zwischen Webhook und API?** Bei der API fragt Ihr System nach, beim Webhook meldet sich der Absender von selbst. Beides lässt sich kombinieren.

**Was passiert, wenn mein Empfänger ausfällt?** Die meisten Absender wiederholen die Zustellung über einen gewissen Zeitraum. Wie lange und wie oft, steht in der Dokumentation des Anbieters. Für Lücken sollten Sie einen Abgleich per API einplanen.

**Reicht es, die Absender-Adresse zu prüfen?** Nein. Absenderadressen lassen sich vortäuschen und ändern sich. Verlässlich ist nur die Signatur mit einem Geheimnis, das nur Sie und der Absender kennen.
