Zur WebsiteZum Dashboard
Themen

Webhooks absichern

Mit dem Signatur-Geheimnis prüfen, dass eine Zustellung wirklich von TickySmart kommt, und das Geheimnis ohne Ausfall wechseln.

Deine Webhook-Adresse ist öffentlich erreichbar, also könnte jeder etwas an sie schicken. Deshalb signieren wir jede Zustellung mit einem Geheimnis, das nur du und wir kennen. Prüf die Signatur, bevor du dem Inhalt vertraust, und verarbeite nichts, bei dem sie nicht stimmt.

Das Signatur-Geheimnis

Jeder Endpunkt hat sein eigenes Geheimnis. Es beginnt mit whsec_ und steht auf der Seite des Endpunkts unter Signatur-Geheimnis.

  1. Klick auf Anzeigen und dann auf Kopieren. Leg das Geheimnis in der Konfiguration deines Servers ab, nicht im Code.
  2. Mit Neues Geheimnis erzeugen wechselst du es, zum Beispiel wenn jemand es gesehen hat, der es nicht kennen sollte.
Karte Signatur-Geheimnis mit dem verdeckten Geheimnis, den Knöpfen Anzeigen und Neues Geheimnis erzeugen und der aufgeklappten Anleitung zur Prüfung

Unter So prüfst du die Signatur steht die Anleitung auch im Dashboard, mit einem Beispiel.

So prüfst du die Signatur

Jede Zustellung hat den Header TickySmart-Signature, zum Beispiel:

TickySmart-Signature: t=1792629600,v1=5257a869e7ec…0ce8e108d8bd

t ist der Zeitpunkt der Zustellung als Unix-Zeit in Sekunden, v1 die Signatur, eine Hexadezimalzahl mit 64 Zeichen (hier gekürzt). So prüfst du sie:

  1. Lies t und alle v1-Werte aus dem Header. Meist ist es einer, beim Wechsel des Geheimnisses zwei.
  2. Bilde einen HMAC mit SHA-256 über t, einen Punkt und den Inhalt der Anfrage, genau so, wie er angekommen ist. Als Schlüssel dient das ganze Geheimnis mit whsec_. Das Ergebnis schreibst du als Hexadezimalzahl in Kleinbuchstaben.
  3. Stimmt es mit einem der v1-Werte überein, kommt die Zustellung von uns. Vergleich mit einer Funktion, die immer gleich lange braucht.
  4. Lehne Zustellungen ab, deren t mehr als fünf Minuten von deiner Uhrzeit abweicht. So kann niemand eine alte Zustellung noch einmal abschicken.

Beispiel in Node.js

Ein kleiner Empfänger ohne weitere Pakete. Das Geheimnis kommt aus der Umgebungsvariable TICKYSMART_WEBHOOK_SECRET:

import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

const SECRET = process.env.TICKYSMART_WEBHOOK_SECRET; // whsec_…

function verify(rawBody, header, secret) {
  const parts = header.split(",");
  const timestamp = Number(
    parts.find((part) => part.startsWith("t="))?.slice(2),
  );
  const signatures = parts
    .filter((part) => part.startsWith("v1="))
    .map((part) => part.slice(3));
  // älter als fünf Minuten: ablehnen
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) {
    return false;
  }
  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return signatures.some(
    (signature) =>
      signature.length === expected.length &&
      timingSafeEqual(Buffer.from(signature), Buffer.from(expected)),
  );
}

createServer((req, res) => {
  let rawBody = "";
  req.setEncoding("utf8");
  req.on("data", (chunk) => (rawBody += chunk));
  req.on("end", () => {
    const header = req.headers["tickysmart-signature"] ?? "";
    if (!verify(rawBody, header, SECRET)) {
      res.writeHead(400).end();
      return;
    }
    const event = JSON.parse(rawBody);
    // event.id merken, doppelte überspringen, dann verarbeiten
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify({ received: true }));
  });
}).listen(3000);

Nutzt du ein Framework wie Express, lies den Inhalt als Rohdaten ein, bevor er als JSON verarbeitet wird. Wird das JSON erst gelesen und dann wieder in Text verwandelt, stimmt die Signatur nicht mehr.

Das Geheimnis wechseln

Nach einem Klick auf Neues Geheimnis erzeugen gilt das alte Geheimnis noch 24 Stunden. So lange tragen alle Zustellungen zwei v1-Signaturen, eine mit dem alten und eine mit dem neuen Geheimnis. Prüfst du wie oben gegen alle v1-Werte, läuft alles weiter, während du das neue Geheimnis auf deinem Server einträgst. Bis wann das alte gilt, steht unter dem Geheimnis.

Häufige Fragen

Die Signatur stimmt nie. Woran liegt das?

Meist an einem dieser Punkte:

  • Der Inhalt wurde vor der Prüfung verändert, etwa als JSON gelesen und neu geschrieben. Prüf immer den unveränderten Text.
  • Du nutzt das Geheimnis eines anderen Endpunkts. Jeder Endpunkt hat sein eigenes.
  • Das whsec_ am Anfang fehlt. Es gehört zum Geheimnis.
  • Die Uhr deines Servers geht falsch, dann greift die Fünf-Minuten-Grenze.

Wer kann das Geheimnis sehen?

Inhaber und Admins auf der Seite des Endpunkts, außerdem Programme mit einem API-Key, der Zugriff auf Webhooks hat. Wir speichern es verschlüsselt.

Kann ich das Geheimnis selbst festlegen?

Nein. Wir erzeugen es beim Anlegen des Endpunkts und bei jedem Wechsel zufällig.

Reicht es nicht, nur Anfragen von euren IP-Adressen anzunehmen?

Feste IP-Adressen für Webhooks nennen wir nicht. Die Signatur ist der sichere Weg: Sie beweist, dass der Inhalt von uns kommt und unterwegs nicht verändert wurde.