Zum Inhalt springen
epilot-berater.de

Blog

epilot Webhooks sicher verarbeiten: Signatur und Idempotenz

Einen epilot Webhook sicher verarbeiten: Signatur prüfen, Zeitstempel kontrollieren, schnell antworten, Retry und Replay beherrschen und Idempotenz umsetzen.

Von Zuletzt aktualisiert: 11 Min. Lesezeit

Einen epilot Webhook sicher zu verarbeiten heißt: die Signatur prüfen, alte Anfragen abweisen, schnell antworten und doppelte Zustellungen erkennen. Dieser Artikel zeigt, wie das mit einem eigenen Dienst in Java und Quarkus aussieht und welche Angaben aus der epilot Dokumentation dafür maßgeblich sind.

Geschrieben hat ihn Marcel R. G. Berger. Ich bin seit über 20 Jahren Softwareentwickler und Softwarearchitekt und arbeite mit epilot als externer Consultant und Entwickler für Apps und Individualisierungen. Der Text richtet sich an IT-Verantwortliche und Entwickler bei Netzbetreibern, Stadtwerken und Energieversorgern, die Daten aus epilot an eigene Systeme weitergeben.

Was ist ein epilot Webhook und wie kommt er an?

Ein epilot Webhook ist ein HTTP-POST, den epilot bei einem Ereignis an eine von Ihnen hinterlegte Adresse sendet. Laut der Übersicht zu den epilot Webhooks enthält die Anfrage einen signierten Payload, und Ihr Server antwortet nach der Prüfung mit 200 OK.

Zwei technische Details sind wichtig, bevor Sie überhaupt eine Zeile Code schreiben.

  • epilot überträgt die Payloads mit Transfer-Encoding: chunked. Der Body kommt in Teilen an, das Ende markiert ein Teil der Länge null. Die meisten aktuellen Server kommen damit ohne Zutun zurecht, Quarkus eingeschlossen.
  • Die Dokumentation zur Payload-Struktur beschreibt ein Objekt metadata mit Angaben wie organization_id, webhook_id und correlation_id sowie ein Objekt entity mit den eigentlichen Daten. Mit einer JSONata-Transformation kann sich diese Form ändern.

Der Weg vom Ereignis bis zur Aktion im Umsystem ist damit klar umrissen. Wie ein Mapper die Daten danach umwandelt und weitergibt, beschreibt der Artikel Mapper zwischen epilot und Umsystemen mit Quarkus. Hier geht es um den Schritt davor: die sichere Annahme.

Warum sollten Sie jede Webhook Signatur prüfen?

Sie sollten jede Webhook Signatur prüfen, weil Ihr Endpunkt sonst jede Anfrage annimmt, die an seine Adresse geht. Die Sicherheitsseite der epilot Webhooks formuliert es deutlich: Signaturen immer vor der Verarbeitung prüfen, weil ein Angreifer sonst gefälschte Anfragen senden könnte.

Bei einem Netzbetreiber ist das keine theoretische Frage. Ein Webhook kann einen Vorgang in einem Fachsystem anlegen, Kundendaten weitergeben oder einen Prozess anstoßen. Eine gefälschte Anfrage, die durchrutscht, erzeugt echte Arbeit und im schlimmsten Fall falsche Daten.

Jede Anfrage von epilot trägt drei Header, die die Prüfung möglich machen.

HeaderInhalt laut epilot DokumentationWofür Sie ihn nutzen
webhook-ideindeutige Kennung der NachrichtTeil der signierten Daten, Kandidat für den Idempotenzschlüssel
webhook-timestampUnix-Zeitstempel in Sekunden, wann signiert wurdeTeil der signierten Daten, Schutz gegen Wiederholungsangriffe
webhook-signaturedurch Leerzeichen getrennte Signaturen v1a,... und v1,...die eigentliche Prüfung

Signiert wird laut Dokumentation der Inhalt webhook-id.webhook-timestamp.body, also die drei Teile mit Punkt verbunden. Dabei zählt der Body genau so, wie er ankommt.

Welche Signaturen sendet epilot und welche sollten Sie prüfen?

epilot sendet zwei Signaturen, und beide beantworten eine andere Frage. Die Sicherheitsseite beschreibt sie so.

SignaturVerfahrenWas sie nachweistSchlüssel
v1aasymmetrisch, Ed25519die Anfrage kommt aus Ihrer Organisationöffentlicher Schlüssel Ihrer Organisation
v1symmetrisch, HMAC-SHA256die Anfrage ist für genau diesen Webhook bestimmtSigning Secret whsec_... aus der Anlage des Webhooks

Für die meisten Fälle empfiehlt epilot die symmetrische Prüfung von v1. Wer beide prüfen will, holt den öffentlichen Schlüssel seiner Organisation über einen dokumentierten Endpunkt ab und speichert ihn zwischen, weil er sich laut Dokumentation selten ändert.

Ein Punkt verdient besondere Aufmerksamkeit: Das Signing Secret wird laut Dokumentation nur einmal zurückgegeben, wenn der Webhook angelegt wird. Geht es verloren, muss der Webhook neu angelegt werden. Legen Sie es deshalb sofort in einem Secret-Speicher ab und nicht in einer Konfigurationsdatei im Repository.

Die Beispiele von epilot nutzen das npm-Paket standardwebhooks. Es setzt die Spezifikation von Standard Webhooks um. Laut Spezifikation ist das Secret nach dem Präfix whsec_ base64-kodiert, und die Signatur hinter v1, ebenfalls. Für einen Java-Dienst bedeutet das: Sie bauen dieselbe Prüfung mit den Bordmitteln des JDK nach.

Wie prüfen Sie die Signatur eines epilot Webhooks in Quarkus?

Sie prüfen die Signatur in Quarkus mit einer eigenen Klasse, die den HMAC-SHA256 über die signierten Daten berechnet und mit dem Wert aus dem Header vergleicht. Dafür reichen javax.crypto.Mac und java.security.MessageDigest aus dem JDK.

@ApplicationScoped
public class SignatureVerifier {

    private static final long TOLERANCE_SECONDS = 300;
    private final byte[] key;

    public SignatureVerifier(@ConfigProperty(name = "epilot.webhook.secret") String secret) {
        if (!secret.startsWith("whsec_")) {
            throw new IllegalStateException("Unexpected secret format");
        }
        this.key = Base64.getDecoder().decode(secret.substring("whsec_".length()));
    }

    public boolean isValid(String id, String timestamp, String signatureHeader, byte[] body) {
        if (id == null || timestamp == null || signatureHeader == null) {
            return false;
        }
        long sentAt;
        try {
            sentAt = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            return false;
        }
        if (Math.abs(Instant.now().getEpochSecond() - sentAt) > TOLERANCE_SECONDS) {
            return false;
        }
        byte[] expected = hmac(id + "." + timestamp + ".", body);
        for (String part : signatureHeader.split(" ")) {
            if (part.startsWith("v1,")) {
                try {
                    byte[] given = Base64.getDecoder().decode(part.substring(3));
                    if (MessageDigest.isEqual(expected, given)) {
                        return true;
                    }
                } catch (IllegalArgumentException e) {
                    return false;
                }
            }
        }
        return false;
    }

    private byte[] hmac(String prefix, byte[] body) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key, "HmacSHA256"));
            mac.update(prefix.getBytes(StandardCharsets.UTF_8));
            return mac.doFinal(body);
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        }
    }
}

Drei Entscheidungen in diesem Code sind kein Zufall.

  • Der Body wird als byte[] übergeben, nicht als geparstes Objekt. Die Signatur gilt für den unveränderten Inhalt, und schon ein anders formatiertes JSON ergibt eine andere Signatur.
  • Der Vergleich läuft über MessageDigest.isEqual. Die Java-Dokumentation zu MessageDigest beschreibt diese Methode als Vergleich, dessen Dauer nicht davon abhängt, an welcher Stelle sich zwei Werte unterscheiden. Die Spezifikation von Standard Webhooks verlangt genau so einen Vergleich.
  • Der Zeitstempel wird gegen eine Toleranz von fünf Minuten geprüft. Das entspricht dem Beispiel für die vollständige Prüfung in der epilot Dokumentation und verhindert, dass eine abgefangene Anfrage später erneut eingespielt wird.

Das Secret kommt über @ConfigProperty aus der Konfiguration, die in Quarkus aus Umgebungsvariablen oder einem Secret-Speicher gespeist werden kann. Wie das funktioniert, beschreibt die Quarkus-Referenz zur Konfiguration.

Welche weiteren Schutzschichten bietet epilot für die Schnittstelle?

Neben der Signatur bietet epilot laut Sicherheitsseite drei Verfahren, mit denen sich der Aufruf Ihres Endpunkts zusätzlich absichern lässt. Sie ersetzen die Signaturprüfung nicht, sie ergänzen sie.

VerfahrenWie epilot es laut Dokumentation umsetzt
API Keyepilot sendet den hinterlegten Schlüssel im Header X-API-Key
Basic Authepilot sendet Benutzername und Passwort base64-kodiert im Header Authorization: Basic
OAuthepilot holt ein Token von Ihrem Autorisierungsserver und sendet es als Authorization: Bearer, die Erneuerung übernimmt epilot

Zugangsdaten müssen dafür nicht im Klartext in der Webhook-Konfiguration stehen. Laut der Dokumentation zu Umgebungen und Secrets in Webhooks lassen sich URL, Header und Felder für Basic Auth, API Key und OAuth über Verweise auf Umgebungsvariablen (env.*) befüllen. Werte vom Typ SecretString werden erst beim Versand aufgelöst und laut Dokumentation nie protokolliert.

Für Unternehmen mit strengen Firewall-Regeln gibt es eine weitere Möglichkeit. Laut der Seite zu statischen IP-Adressen können Enterprise-Kunden auf Anfrage beim Support Webhooks von festen Adressen senden lassen, die sich in der Firewall freigeben lassen.

Wie antwortet der Endpunkt richtig und schnell?

Der Endpunkt antwortet richtig, wenn er erst prüft, dann die Nachricht sicher ablegt und erst danach mit 2xx bestätigt. Die eigentliche Verarbeitung, also Umwandlung und Aufruf der Umsysteme, sollte nicht im selben Aufruf hängen.

Der Grund ist einfach: Ein Umsystem, das gerade langsam ist oder nicht antwortet, darf die Annahme des Webhooks nicht blockieren. Sonst wird aus einem Problem im Fachsystem ein Fehler in epilot. Eine REST-Ressource in Quarkus sieht dafür so aus, wie der Quarkus-Leitfaden zu REST-Diensten es beschreibt.

@Path("/webhooks/epilot")
public class WebhookResource {

    @Inject
    SignatureVerifier verifier;

    @Inject
    Inbox inbox;

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response receive(@HeaderParam("webhook-id") String messageId,
                            @HeaderParam("webhook-timestamp") String timestamp,
                            @HeaderParam("webhook-signature") String signature,
                            byte[] body) {
        if (!verifier.isValid(messageId, timestamp, signature, body)) {
            return Response.status(Response.Status.UNAUTHORIZED).build();
        }
        inbox.storeIfNew(messageId, body);
        return Response.accepted().build();
    }
}

Inbox ist hier eine eigene Klasse des Projekts, keine fertige Bibliothek. Sie schreibt die Nachricht in eine Tabelle, aus der ein Hintergrundprozess sie abarbeitet. Kommt eine bereits bekannte Kennung erneut an, wird sie nicht ein zweites Mal abgelegt, die Antwort bleibt trotzdem erfolgreich.

Die folgende Übersicht zeigt, wie der Endpunkt auf typische Situationen reagieren sollte. Es ist meine Empfehlung für den Aufbau, keine Vorgabe von epilot.

SituationAntwortWas intern passiert
Signatur oder Zeitstempel ungültig401nichts gespeichert, Vorfall protokolliert
Nachricht gültig und neu202 oder 200Nachricht abgelegt, Verarbeitung im Hintergrund
Nachricht gültig, Kennung schon bekannt200nichts Neues abgelegt
Eigene Datenbank nicht erreichbar503Zustellung bleibt in epilot als fehlgeschlagen sichtbar

Was passiert, wenn eine Zustellung fehlschlägt?

Wenn eine Zustellung fehlschlägt, sehen Sie das im Monitoring der Webhooks in epilot und können das Ereignis erneut abspielen. Die Dokumentation zum Monitoring beschreibt, dass jede Anfrage als Ereignis mit Erfolg oder Fehler protokolliert wird.

Dort lassen sich laut Dokumentation Payload, Header und Antwortcode jeder Zustellung einsehen. Ein Replay sendet denselben Payload erneut und legt dabei einen neuen Eintrag an. Die Ereignisse werden standardmäßig vier Wochen aufbewahrt.

Für den Betrieb ergeben sich daraus zwei Regeln.

  1. Ihr Dienst muss eine erneute Zustellung vertragen. Ein Replay ist ausdrücklich vorgesehen, also muss der zweite Empfang derselben Daten harmlos sein.
  2. Die vier Wochen sind ein Zeitfenster für die Fehlersuche, kein Archiv. Was Sie länger nachweisen müssen, gehört in Ihre eigene Protokollierung.

Eine automatische Wiederholung fehlgeschlagener Zustellungen beschreiben die Seiten der epilot Dokumentation, die ich für diesen Artikel geprüft habe, nicht. Ich gehe deshalb nicht davon aus und baue die Verarbeitung so, dass sie mit und ohne Wiederholung korrekt arbeitet.

Wie sorgt Idempotenz dafür, dass nichts doppelt verarbeitet wird?

Idempotenz sorgt dafür, dass eine Nachricht, die zweimal ankommt, nur einmal wirkt. Für einen epilot Webhook heißt das: Ein Replay darf keinen zweiten Vorgang im Fachsystem anlegen.

Als Schlüssel bietet sich die webhook-id an. Die epilot Dokumentation beschreibt sie als eindeutige Kennung der Nachricht, und die Spezifikation von Standard Webhooks empfiehlt, sie als Idempotenzschlüssel zu verwenden. Ich empfehle das ebenfalls, mit zwei Ergänzungen.

  • Ob ein Replay im Monitoring dieselbe webhook-id trägt wie die erste Zustellung, steht nicht in der Dokumentation. Prüfen Sie zusätzlich einen fachlichen Schlüssel aus den Daten, etwa die Kennung des Datensatzes zusammen mit dem Vorgang, den er auslöst.
  • Die Prüfung gehört in dieselbe Transaktion wie das Schreiben. Eine eindeutige Spalte in der Datenbank ist robuster als eine Abfrage vorab, weil sie auch dann greift, wenn zwei Zustellungen gleichzeitig ankommen.

Auch auf der Seite der Umsysteme hilft Idempotenz. Wenn ein Fachsystem selbst einen Schlüssel für Anlagen akzeptiert, sollte der Mapper ihn mitsenden. Dann bleibt eine Wiederholung auch dort folgenlos.

Wie gehen Sie mit Retry in Richtung Umsysteme und epilot um?

Sie wiederholen Aufrufe an Umsysteme gezielt, mit Wartezeit und Obergrenze, und Sie beachten in Richtung epilot den Header Retry-After. Blindes sofortiges Wiederholen macht ein überlastetes System nur langsamer.

Für Aufrufe an Umsysteme bringt Quarkus mit SmallRye Fault Tolerance Annotationen wie @Retry und @Timeout mit. Der Quarkus-Leitfaden zu SmallRye Fault Tolerance zeigt, wie sich Zahl der Versuche, Wartezeit und die Fehler festlegen lassen, bei denen wiederholt wird. Wiederholen sollte der Dienst nur bei vorübergehenden Fehlern, nicht bei abgelehnten Daten.

Schreibt der Dienst Ergebnisse zurück nach epilot, gelten die Rate Limits der Plattform. Die Dokumentation zu Rate Limits beschreibt, dass die API beim Überschreiten mit HTTP 429 antwortet und der Header Retry-After angibt, nach wie vielen Sekunden das Kontingent zurückgesetzt wird. Der Dienst sollte genau so lange warten und nicht früher.

Weil die Verarbeitung im Hintergrund aus der Tabelle läuft, kostet so eine Wartezeit nichts. Der Webhook ist längst bestätigt, und die Nachricht bleibt liegen, bis der nächste Versuch gelingt.

Wann reicht epilot allein, und wann braucht es einen eigenen Dienst?

epilot allein reicht, wenn ein Webhook Daten in einer Richtung an ein Ziel weitergibt und die Felder sich mit Tabellen übersetzen lassen. epilot ist eine hervorragende No-Code- und Low-Code-Plattform, und die Payload-Transformation mit JSONata und Umgebungsvariablen deckt solche Fälle gut ab.

Ein eigener Dienst wird sinnvoll, wenn Ihr Ziel selbst keine Signaturprüfung kann, mehrere Umsysteme in einer Abfolge beteiligt sind oder Ergebnisse zurück nach epilot fließen. Wo diese Grenze typischerweise liegt, beschreibt der Artikel Wann reicht Low-Code bei epilot nicht mehr?. Welche Wege es insgesamt gibt, zeigt der Überblick Umsysteme an epilot anbinden.

So arbeite ich im aktuellen Projekt bei einem Netzbetreiber in Hessen. Viele Prozesse laufen dort außerhalb von epilot, und mehrere Drittsysteme sind über individuell entwickelte Schnittstellen angebunden. Die Mapper-Tools entstehen in Java mit Quarkus, laufen containerisiert und sind skalierbar und wartbar. Mehr dazu steht im Abschnitt Aus der Praxis auf der Startseite.

Dabei ergänze ich das Consulting-Team von epilot, ich ersetze es nicht. epilot bleibt Ihr Ansprechpartner für die Plattform und ihren Standardumfang. Ich setze dort an, wo klassische Entwicklung nötig wird. Welche Leistungen das umfasst, steht im Abschnitt Was ich für epilot-Kunden mache.

Wie starten Sie mit einer sicheren Webhook-Verarbeitung?

Sie starten mit einem kostenlosen Erstgespräch, in dem wir Ihre Webhooks, Umsysteme und Anforderungen an den Betrieb durchgehen. Danach ist klar, ob die Werkzeuge von epilot reichen oder ob ein eigener Dienst sinnvoll ist.

  1. Erstgespräch: kostenlos und unverbindlich, 30 Minuten über Microsoft Teams.
  2. Individuelles Angebot: zugeschnitten auf Ihre Webhooks, Systeme und Anforderungen.
  3. Umsetzung: Signaturprüfung, Ablage, Verarbeitung und Tests werden entwickelt und mit Ihren Systemen in Betrieb genommen.

Der Code gehört Ihnen. Auf Wunsch übergebe ich ihn in Ihr eigenes Repository, alternativ betreue ich ihn über einen Wartungsvertrag. Warum das wichtig ist, erklärt der Artikel Wem gehört der Integrationscode?. Vertragspartner ist die DigitalFreedom Global LLC, Ihre Daten bleiben in Europa, und wo nötig schließen wir einen AVV. Die Einzelheiten stehen im Abschnitt Vertragspartner.

Alle Leistungen biete ich auf Basis eines individuellen Angebots an. Antworten auf weitere Fragen finden Sie in den häufigen Fragen auf der Startseite, einen Termin wählen Sie direkt im Buchungskalender.

Häufige Fragen zu diesem Thema

Muss ich die Signatur eines epilot Webhooks wirklich prüfen?
Ja. Die Sicherheitsseite der epilot Dokumentation empfiehlt ausdrücklich, Signaturen immer vor der Verarbeitung zu prüfen. Ohne Prüfung könnte jeder, der die Adresse kennt, gefälschte Anfragen an Ihren Endpunkt senden.
Welches Verfahren nutzt epilot für die Webhook Signatur?
epilot sendet laut Dokumentation zwei Signaturen: v1a ist asymmetrisch mit Ed25519 und weist nach, dass die Anfrage aus Ihrer Organisation kommt. v1 ist symmetrisch mit HMAC-SHA256 und weist nach, dass sie für genau diesen Webhook bestimmt ist. Beide werden über webhook-id, Zeitstempel und Body berechnet.
Wiederholt epilot fehlgeschlagene Webhooks?
Die Dokumentation beschreibt, dass jede Zustellung als Ereignis mit Status protokolliert wird und sich im Monitoring erneut abspielen lässt. Eine automatische Wiederholung beschreiben die Seiten, die ich geprüft habe, nicht. Planen Sie die Verarbeitung so, dass sie doppelte Zustellungen in jedem Fall verträgt.
Reicht die webhook-id als Schutz gegen doppelte Verarbeitung?
Sie ist ein guter erster Schlüssel, und der Standard Webhooks empfiehlt sie als Idempotenzschlüssel. Ob ein Replay im Monitoring dieselbe webhook-id trägt, beschreibt die epilot Dokumentation nicht. Ich empfehle deshalb zusätzlich einen fachlichen Schlüssel aus den Daten.
Kann ich die Webhooks auch ohne eigenen Dienst verarbeiten?
Für einfache Fälle ja. epilot bietet eine Payload-Transformation mit JSONata und Umgebungsvariablen an. Ein eigener Dienst wird sinnvoll, wenn mehrere Umsysteme beteiligt sind, Daten geprüft, angereichert oder zurück nach epilot geschrieben werden.
Was kostet die Umsetzung einer sicheren Webhook-Verarbeitung?
Das hängt von der Zahl der Webhooks, der Umsysteme und den Anforderungen an den Betrieb ab. Nach dem kostenlosen Erstgespräch erhalten Sie ein individuelles Angebot.

Passt das zu Ihrem Vorhaben?

Im kostenlosen Erstgespräch über Microsoft Teams klären wir in 30 Minuten, wo der Standard von epilot bei Ihnen endet. Danach erhalten Sie ein individuelles Angebot.

Termin buchen

Veröffentlicht: . Zuletzt aktualisiert: .