Webhook-Sicherheit
Jede Webhook-Zustellung für ein Abonnement mitwebhook_secret enthält eine Signatur, die Sie überprüfen sollten, um sicherzustellen, dass die Anfrage von RxScale stammt und nicht manipuliert wurde.
Signaturüberprüfung
Jede Webhook-Anfrage enthält ihre Signatur im HeaderX-Webhook-Signature:
- Die Signatur ist der HMAC-SHA256 des unveränderten Anfragekörpers (genau so, wie er empfangen wurde), kodiert als Hexadezimalzeichenkette in Kleinbuchstaben (64 Zeichen). Es gibt kein Präfix wie
sha256=. - Der Schlüssel ist das
webhook_secret, das Sie bei der Registrierung des Abonnements erhalten haben, als UTF-8-Zeichenkette genau so verwendet, wie es zurückgegeben wurde. Dekodieren Sie es nicht. - Jedes Abonnement (Ziel-URL, Ereignistyp und Apotheken-Zuordnung) hat ein eigenes
webhook_secret. Wenn Sie mehrere Ereignistypen am selben Endpoint empfangen, wählen Sie anhand des HeadersX-Webhook-Eventdas Secret des passenden Abonnements. - Es gibt keinen separaten Zeitstempel-Header. Das Feld
timestampim Anfragekörper ist durch die Signatur abgedeckt.
Beispiel (Python)
Beispiel (Node.js)
Test-Webhooks enthalten den Header
X-Webhook-Test: true, damit Sie sie von echten Zustellungen unterscheiden können. Über die Karte Webhook testen im Apothekenportal gesendete Test-Webhooks sind nicht signiert. Über die Management API gesendete Test-Webhooks werden wie eine echte Zustellung signiert, sofern ein aktives Abonnement zu Ziel-URL und Ereignistyp passt; andernfalls sind sie unsigniert.Webhook-Secret
- Das
webhook_secretwird nur einmal zurückgegeben: in der Antwort aufPOST /v1/external_pharmacy_api/webhooks/oder im Apothekenportal direkt nach der Registrierung eines Webhooks. Es wird verschlüsselt gespeichert und kann später nicht abgerufen werden —GET /v1/external_pharmacy_api/webhooks/gibt es nie zurück. - Jedes Abonnement hat ein eigenes Secret.
X-Webhook-Eventnennt den Ereignistyp einer Zustellung, aber nichts identifiziert das Abonnement selbst. Abonnements mit demselben Ereignistyp — zum Beispiel eines pro Apotheke einer Gruppe — lassen sich daher nicht unterscheiden. Verwenden Sie für diese jeweils eine eigene Ziel-URL (zum Beispiel einen anderen Pfad oder Query-Parameter), damit Sie immer wissen, mit welchem Secret Sie prüfen müssen. - Auch der Wert eines benutzerdefinierten Headers (
header_value) wird nie zurückgegeben, nur sein Name (header_key).
Secret rotieren
Es gibt keinen eigenen Endpoint für die Rotation. Sie haben zwei Möglichkeiten: Dasselbe Abonnement erneut registrieren. Senden SiePOST /v1/external_pharmacy_api/webhooks/ erneut mit denselben Werten für notification_type, target und pharmacy_uid. RxScale behält die uid des Abonnements bei und gibt ein neues webhook_secret zurück, das sofort gilt — das alte Secret funktioniert nicht mehr. Jede Zustellung wird beim Versand signiert, daher verwenden alle Zustellungen nach dieser Anfrage, auch Wiederholungen früherer Ereignisse, das neue Secret. Speichern Sie es sofort. Die Anfrage ersetzt außerdem den benutzerdefinierten Header: Wenn Sie header_key und header_value weglassen, wird er entfernt.
Mit Überlappung rotieren. Registrieren Sie ein zweites Abonnement für dasselbe Ereignis mit einer anderen Ziel-URL und prüfen Sie die Zustellungen an diese URL mit dem Secret des neuen Abonnements. Löschen Sie anschließend das alte Abonnement mit DELETE /v1/external_pharmacy_api/webhooks/{subscription_uid}. Solange beide Abonnements bestehen, wird jedes Ereignis an beide zugestellt, jeweils signiert mit dem Secret des jeweiligen Abonnements.
Details zu den Endpoints finden Sie unter Webhooks (External Pharmacy API).
Best Practices
Signaturen immer überprüfen
Signaturen immer überprüfen
Verarbeiten Sie niemals Webhook-Payloads, ohne vorher die Signatur zu überprüfen. Dies schützt vor gefälschten Anfragen.
Konstantzeitvergleich verwenden
Konstantzeitvergleich verwenden
Verwenden Sie immer
hmac.compare_digest (Python) oder crypto.timingSafeEqual (Node.js), um Timing-Angriffe zu verhindern.Schnell antworten
Schnell antworten
Sie müssen innerhalb des Zustellungs-Timeouts von 30 Sekunden eine
2xx-Antwort zurücksenden. Eine langsamere Antwort (oder keine Antwort) gilt als fehlgeschlagene Zustellung und wird mit exponentiellem Backoff wiederholt — siehe Wiederholungsrichtlinie.Wir empfehlen dennoch, deutlich schneller zu bestätigen — ein guter Zielwert liegt unter 5 Sekunden —, indem Sie 2xx zurückgeben, sobald Sie die Payload gespeichert haben, und aufwändige Verarbeitung anschließend asynchron durchführen. Die 5 Sekunden sind eine Empfehlung, keine Anforderung; erzwungen wird nur das Timeout von 30 Sekunden.Ereignisse idempotent verarbeiten
Ereignisse idempotent verarbeiten
Derselbe Stand kann mehrfach zugestellt werden, und Zustellungen können in anderer Reihenfolge eintreffen. Verarbeiten Sie Ereignisse anhand von
data.uid und behandeln Sie jede Payload als aktuellen Gesamtstand. Deduplizieren Sie nicht anhand von timestamp — der Wert ändert sich bei jedem Zustellversuch. Siehe Ereignisse idempotent verarbeiten.Wiederholt eingespielte Anfragen abweisen
Wiederholt eingespielte Anfragen abweisen
Der
timestamp im Anfragekörper wird beim Versand eines Zustellversuchs gesetzt und ist durch die Signatur abgedeckt. Um das erneute Einspielen einer abgefangenen Anfrage zu erschweren, können Sie Zustellungen ablehnen, deren timestamp um mehr als einige Minuten von Ihrer Serverzeit abweicht. Wiederholungen sind davon nicht betroffen, da jeder Versuch einen neuen timestamp trägt.