Die Bedeutung der Überprüfung von Webhook-Signaturen
Marcelo Oliveira
29. Juni 2023
0 Min. LesezeitWebhooks sind eine Callback-Integrationstechnik, mit der Informationen wie Ereignisbenachrichtigungen nahezu in Echtzeit gesendet und empfangen werden. Sie können durch Anwendungsereignisse ausgelöst werden und Daten über HTTP an eine andere Anwendung oder eine Drittanbieter-API übertragen. Sie können eine Webhook-URL konfigurieren und externe Beteiligte anbinden, um Workflows anzupassen, zu erweitern oder zu ändern.
Webhooks sind unter Umständen signiert. Aus Sicherheitsgründen empfiehlt es sich jedoch, eine überprüfbare Signatur einzubinden, damit der Empfänger bestätigen kann, dass die Anfrage von der erwarteten Webhook-Quelle stammt.
Hier sind einige Beispiele dafür, wie Webhooks heute eingesetzt werden:
Benachrichtigungen auf einer Git-basierten Versionskontrollplattform, wenn ein Entwickler Code in das Repository gepusht hat.
Benachrichtigungen, wenn ein Nutzer auf einer Messaging-Plattform auf einen Nachrichten-Thread antwortet.
Benachrichtigungen eines Zahlungsdienstes zur Bestätigung, dass eine Zahlungsmethode für einen Einkauf im Einzelhandel autorisiert wurde.
Einem Bericht von Verizon zufolge entfielen 2022 62 % aller Vorfälle mit Systemeinbrüchen auf Supply-Chain-Angriffe. Während des gesamten Softwareentwicklungszyklus ist es entscheidend, die Sicherheit aller an Entwicklung und Bereitstellung beteiligten Komponenten und Verfahren zu gewährleisten. Werden Schwachstellen ausgenutzt, können die beteiligten Partner kompromittiert und die gesamte Software-Supply-Chain gestört werden.
Um diesen Bedrohungen zu begegnen und die Supply-Chain-Sicherheit zu gewährleisten, setzen Anbieter in der Supply Chain Webhook-Signaturen ein. Mit CircleCI können Sie beispielsweise Webhook-Signaturen verwenden, um ein Secret zu validieren und sicherzustellen, dass nur CircleCI – und kein Angreifer – Ihren Webhook aufruft. Travis CI stellt im Rahmen der Webhook-Überprüfung ebenfalls einen HTTP-Signatur-Header bereit.
In dieser Anleitung implementieren wir einen GitHub-Webhook in Node.js, der erkennt, wenn Nutzer Code in ein Repository pushen.
Voraussetzungen
Für die Anleitung benötigen Sie Folgendes:
Laden Sie Node.js und npm herunter und installieren Sie sie.
Installieren Sie ngrok, ein Reverse-Proxy-Tool, mit dem Sie einen sicheren Tunnel von einer öffentlichen URL zu Ihrer lokalen Node.js-App öffnen.
Installieren Sie Postman, eine Plattform, mit der Sie HTTP-Anfragen erstellen und die Ergebnisse einfach überprüfen können.
Webhook-Signaturen mit Node.js überprüfen
Erstellen Sie auf Ihrem lokalen Rechner einen neuen Ordner und darin eine neue Datei namens webhook.js mit dem folgenden Code. Denken Sie daran, den Wert der Konstante repo durch den Pfad zu Ihrem lokalen Repository zu ersetzen:
Führen Sie anschließend die folgende Zeile in Ihrem Terminal aus:
Mit der Datei webhook.js möchten wir Folgendes erreichen:
Die Konstante
repospeichert den Pfad zu dem Ordner mit dem Repository, das wir überwachen möchten.Die Funktion
createServerstartet auf unserem Rechner einen HTTP-Server auf Port 8080. Die Webhook-App läuft nun und kann Anfragen am Endpunkt http://localhost:8080 empfangen.Sobald die eingehende Anfrage verarbeitet wird, speichert die Konstante body den größten Teil der Anfrage. Eine Anfrage an unseren Endpunkt bedeutet, dass wir eine Benachrichtigung vom GitHub-Webhook an unsere lokale Payload-URL unter http://localhost:8080 erhalten. Sie informiert uns darüber, dass ein Nutzer Code in das von uns überwachte Repository gepusht hat.
Wir möchten sicherstellen, dass der Push ausdrücklich an den Main-Branch erfolgt ist. Dazu parsen wir die JSON-Daten der Anfrage in ein body-Objekt und verwenden die Konstante
isMain, um anzugeben, ob die Referenz mit dem Main-Branch übereinstimmt.Wird die Anfrage akzeptiert, führen wir einen Befehl aus, um zum lokalen Repository zu wechseln. Abschließend führen wir die Befehle
git fetchundgit pullaus, um es zu aktualisieren.
Eine bösartige Anfrage mit Postman simulieren
Wir entwickeln die Node.js-Anwendung für die Zusammenarbeit mit GitHub. Bevor wir GitHub verwenden, simulieren wir jedoch mit Postman eine bösartige Anfrage, um zu zeigen, wie anfällig unsere Anwendung ist. So wird die Bedeutung von Signaturen deutlich.
Öffnen Sie die Postman-App und konfigurieren Sie eine neue HTTP-POST-Anfrage an unsere lokale Payload-URL http://localhost:8080.
Fügen Sie auf dem Tab Body den folgenden Code ein:

Klicken Sie auf Send. Öffnen Sie das Terminal, in dem Node.js läuft, und sehen Sie sich an, was passiert ist:

Wie Sie sehen, akzeptiert die Node.js-App eine Anfrage von einer anderen Quelle als GitHub. Das ist gefährlich. Wir müssen in unserem Code Webhook-Signaturen implementieren, um überprüfen zu können, ob der Absender legitim ist.
ngrok konfigurieren
In diesem Szenario führen wir die Node.js-App lokal aus. GitHub kann jedoch den Endpunkt http://localhost:8080 auf Ihrem Rechner nicht erreichen, um eine Webhook-Anfrage zu senden. Das ließe sich lösen, indem wir die Node.js-App auf einem öffentlichen Server veröffentlichen. Allerdings ist es einfacher, Node.js auf dem Entwicklungsrechner auszuführen und zu debuggen, als die Anwendung auf einem Webserver oder in der Cloud bereitzustellen. Deshalb verwenden wir ngrok. ngrok ist ein Reverse-Proxy-Tool, das einen sicheren Tunnel von einer zufälligen öffentlichen URL zu Ihrem lokalen Webhook-Endpunkt öffnet.
Öffnen Sie auf Ihrem Rechner ein Terminalfenster und führen Sie den folgenden ngrok-Befehl aus, um den lokalen Port 8080 freizugeben:
ngrok bestätigt, dass der lokale Port 8080 über einen sicheren Tunnel mit einer zufälligen öffentlichen URL freigegeben wurde:
Als Nächstes verwenden wir die oben angezeigte öffentliche ngrok-Subdomain, um einen GitHub-Webhook zu konfigurieren.
Einen GitHub-Webhook konfigurieren
Rufen Sie die GitHub-Seite zum Erstellen eines neuen Repositorys auf und fügen Sie ein neues Repository hinzu, um den Webhook zu konfigurieren:

Klicken Sie in der oberen Leiste auf Settings. Wählen Sie dann in der linken Liste die Aktion Webhooks aus und klicken Sie auf Add webhook:

Daraufhin wird das Formular Add webhook geöffnet. Hier konfigurieren wir einen Webhook für ein Ereignis, bei dem ein Entwickler Code in das Repository gepusht hat.

Füllen Sie das Formular nun wie folgt aus:
Geben Sie im Feld Payload URL die ngrok-Adresse relativ zum Endpunkt Ihrer lokalen Node.js-Anwendung ein.
Wählen Sie im Feld Content type den Eintrag „application/json“ aus. Damit geben Sie an, welche Art von Inhalt GitHub an den Endpunkt sendet.
Geben Sie im Feld Secret „some-webhook-secret“ ein. Denken Sie daran, dass Sie in der Produktionsumgebung stärkere Passwörter oder Passphrasen benötigen, um die Sicherheit zu gewährleisten.
Wählen Sie unter Which events would you like to trigger this webhook die Option Just the push event aus. Dadurch wird der GitHub-Webhook für einen Repository-Push eingerichtet, den Sie zur Überprüfung auslösen können.
Hinweis: Es empfiehlt sich immer, die SSL-Überprüfung (Secure Socket Layer) in der Webhook-Konfiguration zu aktivieren. So stellen Unternehmen sicher, dass die zwischen Systemen übertragenen Daten geschützt sind und nicht von Unbefugten abgefangen werden können.
Klicken Sie nach dem Ausfüllen des Formulars auf Add webhook.

Den Webhook absichern
Wir müssen das erforderliche Node.js-Modul crypto hinzufügen, um verschlüsselte Daten zu verarbeiten. Fügen Sie diese Zeile am Anfang der Datei webhook.js ein:
Deklarieren Sie als Nächstes eine Konstante, in der dieselbe Secret-Zeichenfolge gespeichert wird, die Sie für Ihren GitHub-Webhook konfiguriert haben:
Hinweis: Secrets sollten niemals fest im Code hinterlegt werden. Diese Methode dient ausschließlich der Demonstration.
Am Anfang der Funktion req.on deklarieren wir eine Konstante, in der der Wert der mit HMAC berechneten SHA256-Signatur gespeichert wird. Ermitteln Sie die HMAC-SHA256-Signatur, indem Sie die kryptografische SHA256-Hashfunktion auf den gemeinsamen geheimen Schlüssel und den von der GitHub-Anfrage übermittelten Wert anwenden (hier dargestellt durch den Parameter chunk):
Als Nächstes deklarieren wir innerhalb des Ereignisses req.on die Konstante isAllowed, um anzugeben, ob der x-hub-signature-256-Header mit der von uns berechneten Signatur übereinstimmt. Verwenden Sie die Funktion crypto.timingSafeEqual, die zwei Variablen vergleicht, ohne Zeitinformationen offenzulegen, die einem Angreifer dabei helfen könnten, einen der Werte zu erraten:
Stimmen die Werte im obigen Code überein, können wir sicher sein, dass die Anfrage von GitHub und nicht von einem Angreifer stammt. Kommt die Anfrage nicht von GitHub, wird eine HTTP-401-Antwort „Unauthorized“ zurückgegeben.
Der folgende Code setzt einige Best Practices um, die Sie bei der Entwicklung Ihrer Webhooks ebenfalls anwenden sollten:
Wenn Sie Zeichenfolgenoperationen zur Berechnung der Webhook-Signaturen durchführen, verarbeiten Sie die Payload immer als UTF-8, da sie Unicode-Zeichen enthalten kann. Wenn Sie außerdem bestimmte Erwartungen an die Darstellung Ihrer Daten haben, können Sie eine Validierungsmethode implementieren, um sicherzustellen, dass die empfangenen Daten diesen Erwartungen entsprechen.
Einige Webhook-Anbieter wie GitHub fügen den Anfragen möglicherweise einen
x-hub-signature-Header hinzu. Der Headerx-hub-signaturewird mit SHA-1 generiert. Dieser Hashing-Algorithmus gilt als schwach, wird jedoch aus Gründen der Abwärtskompatibilität weiterhin unterstützt. Verwenden Sie nach Möglichkeit den Headerx-hub-signature-256, der mit dem starken SHA-256-Algorithmus generiert wird.Beginnen Sie die Hash-Signatur mit
"sha256="und verwenden Sie das Secret des Payload-Bodys der Anfrage.Verwenden Sie statt eines einfachen Gleichheitsoperators (
"=="oder"===") eine sichere Vergleichsmethode, die das Risiko von Timing-Angriffen verringert. Alle modernen Programmiersprachen und Plattformen bieten mindestens eine solche sichere Methode. In diesem Node.js-Projekt verwenden wir die Funktioncrypto.timingSafeEqual.
Den vollständigen Code für webhook.js finden Sie hier.
Weitere Informationen finden Sie in der GitHub-Dokumentation zur Absicherung von Webhooks.
Den abgesicherten Webhook-Endpunkt testen
Da Sie den Code geändert haben, sollten Sie Ihren lokalen Node-Server neu starten. Beenden Sie den Node-Server in der Konsole mit Ctrl+C (oder Command+C auf dem Mac) und führen Sie den Befehl node webhook.js erneut aus.
Wechseln Sie anschließend zurück zur Postman-App und senden Sie die Anfrage erneut. Jetzt antwortet die Node.js-App mit dem HTTP-Statuscode 401 „Unauthorized“:

Öffnen Sie GitHub und fügen Sie eine Datei namens README.md hinzu, falls noch keine vorhanden ist, oder öffnen Sie die vorhandene Datei. Fügen Sie dann den folgenden Text hinzu oder ändern Sie ihn entsprechend:

Klicken Sie abschließend auf Commit changes, um die Änderungen in den Main-Branch des Repositorys zu pushen:

Wechseln Sie nun zurück zum Node.js-Terminal und bestätigen Sie, dass das Repository erfolgreich abgerufen und aktualisiert wurde:

Fazit
Webhooks ermöglichen die Kommunikation zwischen Tools und stellen Ereignisdaten in Echtzeit bereit. Sie können sie in Systeme integrieren, um flexible und erweiterte kollaborative Workflows zu schaffen, die mit APIs mit vordefinierten Parametern nicht möglich wären.
Da Webhooks standardmäßig offen sind, sind sie anfällig für Sicherheitsbedrohungen. Daher ist es unerlässlich, geeignete Maßnahmen zu ergreifen, um Supply-Chain-Angriffe zu verhindern.
Webhook-Signaturen sind die empfohlene Lösung, da Webhook-Anbieter Secrets konfigurieren und Signaturen mit starken Hashing-Algorithmen generieren können. Signaturen sind eine einfache und sichere Möglichkeit, sicherzustellen, dass die Datenkommunikation über Webhooks ausschließlich zwischen verifizierten und vertrauenswürdigen Partnern stattfindet.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.



