fix(webhooks): close DNS rebinding, time out deliveries, sign with timestamp

- Deliveries connect through a lookup hook that checks the address
  actually dialled, so a host that resolves to a private address after
  registration (DNS rebinding) or a tampered row cannot reach internal
  services. Redirects are still not followed.
- More blocked ranges (CGNAT/100.64/10 incl. cloud metadata, multicast,
  reserved, benchmarking, NAT64, 6to4, site-local, IPv4-mapped IPv6);
  only ports 80/443, no credentials in the URL, https in production.
- 5 s timeout, response body discarded; dispatch no longer waits for
  receivers, so a hanging endpoint cannot stall the user's request.
- Signature covers "timestamp.deliveryId.body" with X-ApiCore-Timestamp
  and X-ApiCore-Delivery headers, so receivers can reject replays.
- Signing secrets are stored AES-256-GCM-encrypted; older plain rows still
  work.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-01 02:17:40 +02:00
1 parent ebeb499a18
commit 79d20ee64e
8 files changed
+281 -72

No files matched your search

@@ -91,6 +91,13 @@ export function CreateWebhookDialog() {
<code className="rounded bg-muted px-1 py-0.5">X-ApiCore-Signature</code>-Kopfzeile
(HMAC-SHA256) jeder Auslieferung.
</p>
<p className="text-sm text-muted-foreground">
Signiert wird{" "}
<code className="rounded bg-muted px-1 py-0.5">Zeitstempel.Auslieferungs-ID.Body</code>{" "}
aus <code className="rounded bg-muted px-1 py-0.5">X-ApiCore-Timestamp</code> und{" "}
<code className="rounded bg-muted px-1 py-0.5">X-ApiCore-Delivery</code>. Der Empfänger
sollte ältere als 5 Minuten und bereits gesehene IDs ablehnen.
</p>
<div className="flex items-center gap-2 rounded-lg border bg-muted/50 p-3">
<code className="flex-1 overflow-x-auto text-xs">{createdSecret}</code>
<Button type="button" variant="outline" size="sm" onClick={() => void copySecret()}>