⌘K

Webhook-Signaturen prüfen

Die Herkunft von Webhooks sicherzustellen ist entscheidend. Ein Angreifer könnte Ihnen einen gefälschten Payload senden (z. B. {"event":"quote.paid"}), damit Sie etwas freigeben, ohne dass eine echte Zahlung vorliegt. Deshalb müssen Sie die Signatur prüfen.

Wie Cord signiert

Jede Zustellung enthält zwei Signaturen des unveränderten Rohinhalts, damit Sie ohne Umstellungsfenster migrieren können:

  • X-Cord-Signature-V1 (empfohlen): t=<Unix-Zeitstempel>,v1=<hex-HMAC-SHA256 von "{t}.{Body}">. Der Zeitstempel ist Teil des signierten Inhalts, sodass Sie eine alte, erneut abgespielte Zustellung ablehnen können (Replay-Schutz). Während einer Secret-Rotation kann sie zwei v1=-Paare enthalten; es genügt, wenn eines davon passt.
  • X-Cord-Signature (Legacy, aus Kompatibilitätsgründen beibehalten): sha256=<hex-HMAC-SHA256 des Body>, ohne Zeitstempel.

Neben der Signatur enthält jede Zustellung X-Cord-Event (Name des Ereignisses), X-Cord-Event-Id (stabile ID des Ereignisses, gleich bei Wiederholungen und bei einer manuellen erneuten Zustellung – verwenden Sie sie zum Deduplizieren), X-Cord-Delivery-Id (ändert sich bei jedem Versuch) und Idempotency-Key (wiederholt den Wert von X-Cord-Event-Id, für Frameworks, die ihn automatisch lesen).

Mit dem offiziellen SDK (empfohlen)

@flouviahq/node enthält den fertigen Prüfer: Er verlangt X-Cord-Signature-V1, vergleicht in konstanter Zeit und lehnt eine Zustellung ab, die älter als 300 Sekunden ist (konfigurierbar mit tolerance). Die Legacy-Signatur wird nur akzeptiert, wenn Sie sie mit allowLegacySignature: true anfordern.

import { constructEvent, CordWebhookSignatureError } from '@flouviahq/node';

// Beispiel mit einem Route Handler von Next.js – req.text() ist bereits der Rohinhalt
export async function POST(req: Request) {
  try {
    const event = await constructEvent(await req.text(), req.headers, process.env.CORD_WEBHOOK_SECRET!);
    // event.event, event.data – der Ereigniskatalog steht in der Webhook-Anleitung
    return new Response('ok');
  } catch (err) {
    if (err instanceof CordWebhookSignatureError) return new Response('Invalid signature', { status: 400 });
    throw err;
  }
}

npx @flouviahq/cli init legt diese Route für Sie an, und mit npx @flouviahq/cli listen testen Sie sie auf Ihrem Rechner mit echten Signaturen.

Manuelle Prüfung in Node.js (Express)

Wenn Sie das SDK nicht verwenden, können Sie die Legacy-Signatur von Hand prüfen:

const crypto = require('crypto');

app.post('/webhook/cord', express.raw({ type: 'application/json' }), (req, res) => {
  const received = req.headers['x-cord-signature'] || '';   // "sha256=<hex>"
  const secret = process.env.CORD_WEBHOOK_SECRET;

  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(req.body)            // req.body ist der rohe Buffer, nicht geparst
    .digest('hex');

  // Vergleich in konstanter Zeit (beide Buffer müssen gleich lang sein)
  const ok = received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));

  if (!ok) return res.status(401).send('Invalid signature');

  const event = req.headers['x-cord-event'];
  const payload = JSON.parse(req.body.toString('utf8'));
  // ... Ereignis verarbeiten (payload.data.id, payload.data.folio usw.)
  res.status(200).send('Received');
});

Wichtig: Signieren Sie über den rohen Body (verwenden Sie express.raw, nicht express.json), sonst stimmt der Hash nicht überein. Um X-Cord-Signature-V1 von Hand zu prüfen, berechnen Sie den HMAC über "{timestamp}.{body}" statt nur über den Body und prüfen Sie, dass der Zeitstempel innerhalb Ihrer Toleranz liegt – oder verwenden Sie das SDK, das das bereits für Sie erledigt.


Brauchen Sie weitere Hilfe?

Wenden Sie sich an unser Expertenteam.

Sie sehen, was Sie zahlen

Veröffentlichte Preise, bei denen die Gebühr für jede Zahlung klar ausgewiesen ist.

Mehr zu den Preisen →

Legen Sie los

Starten Sie im kostenlosen Tarif und versenden Sie Ihre Angebote.

Integrationsoptionen →