Skip to main content

Die Bedeutung der Überprüfung von Webhook-Signaturen

Artikel von

Marcelo Oliveira

blog feature playwright tests

29. Juni 2023

0 Min. Lesezeit

Webhooks 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:

const http = require('http');
const exec = require('child_process').exec;

const repo = "C:\\Users\\your-user-name\\Documents\\GitHub\\webhook-test";

http
  .createServer((req, res) => {
    req.on('data', chunk => {

      const body = JSON.parse(chunk);

      console.log(body);

      const isMain = body?.ref === 'refs/heads/main';

      if (isMain) {
        try {
            console.log('Push event detected. Pulling repository updates...');
            exec(`cd ${repo} && git fetch && git pull`);
            console.log('Git repository pulled successfully.');
        } catch (error) {
          console.log(error);
        }
      }
      res.end();
    });
  })
  .listen(8080);

Führen Sie anschließend die folgende Zeile in Ihrem Terminal aus:

node webhook.js

Mit der Datei webhook.js möchten wir Folgendes erreichen:

  1. Die Konstante repo speichert den Pfad zu dem Ordner mit dem Repository, das wir überwachen möchten.

  2. Die Funktion createServer startet auf unserem Rechner einen HTTP-Server auf Port 8080. Die Webhook-App läuft nun und kann Anfragen am Endpunkt http://localhost:8080 empfangen.

  3. 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.

  4. 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.

  5. Wird die Anfrage akzeptiert, führen wir einen Befehl aus, um zum lokalen Repository zu wechseln. Abschließend führen wir die Befehle git fetch und git pull aus, 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:

{
    "ref": "refs/heads/main",
    "comment": "hi, I'm a malicious request..."
}
Postman-Anfrage mit einer Webhook-Nutzlast und einer 401-Fehlermeldung „Unauthorized“, die besagt, dass die Webhook-Signatur nicht mit dem Hash der Nutzlast übereinstimmt.

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

Terminalausgabe mit einem erkannten Git-Push-Ereignis, dem Abruf von Repository-Updates und einem erfolgreichen Repository-Pull.

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 http 8080

ngrok bestätigt, dass der lokale Port 8080 über einen sicheren Tunnel mit einer zufälligen öffentlichen URL freigegeben wurde:

Forwarding https://<<YOUR-RANDOM-SUBDOMAIN>>.sa.ngrok.io -> http://localhost:8080

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:

Repository-Erstellungsformular mit dem Titel „Neues Repository erstellen“, einem unkenntlich gemachten Inhaber und dem Repository-Namen „webhook-test“.

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:

GitHub-Repositoryseite „Settings“ mit ausgewähltem Menüpunkt „Webhooks“ in der Seitenleiste. Pfeile zeigen auf die Überschrift „Webhooks“ und die Schaltfläche „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.

GitHub-Formular zum Hinzufügen eines Webhooks mit Feldern für eine Payload-URL, den JSON-Inhaltstyp und ein Secret, Optionen für Ereignisauslöser sowie einem Kontrollkästchen für „Aktiv“.

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.

GitHub-Formular zum Hinzufügen eines Webhooks mit Pfeilen, die auf die Payload-URL, den JSON-Inhaltstyp, das Secret und die Auswahl des Push-Ereignisses hinweisen.

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:

const crypto = require('crypto');

Deklarieren Sie als Nächstes eine Konstante, in der dieselbe Secret-Zeichenfolge gespeichert wird, die Sie für Ihren GitHub-Webhook konfiguriert haben:

const secret = 'some-webhook-secret';

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):

    req.on('data', chunk => {

      const hashAlgorithm = 'sha256'
      const signature = Buffer.from(req.headers['x-hub-signature-256'] || '', 'utf8')
      const hmac = crypto.createHmac(hashAlgorithm, secret)
      const digest = Buffer.from(hashAlgorithm + '=' + hmac.update(chunk).digest('hex'), 'utf8')

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:

      const isAllowed = (signature.length === digest.length && crypto.timingSafeEqual(digest, signature));
      if (!isAllowed) {
        const msg = 'Webhook signature does not match the hash of the payload';
        console.log(msg);
        res.writeHead(401)
        res.end(msg)
        return;
      }

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 Header x-hub-signature wird 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 Header x-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 Funktion crypto.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“:

Postman-Anfrage mit einer schädlichen JSON-Nutzlast und einer 401-Antwort „Unauthorized“ mit dem Hinweis, dass die Webhook-Signatur nicht mit dem Hash der Nutzlast übereinstimmt

Ö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:

GitHub-README.md-Editor mit einem Repository namens webhook-test und dem Text „My WebHook Test“ und „This is a webhook test.“

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

GitHub-Dialog „Änderungen committen“ mit der Meldung „README.md aktualisieren“ und der Option, direkt in den Main-Branch zu committen, hervorgehoben durch einen violetten Pfeil.

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

Terminalausgabe mit einem GitHub-Webhook-Push-Ereignis, einer Repository-Aktualisierung und einem erfolgreichen Git-Pull.

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.

Weiterlesen

blog feature snyk container custom base recommendations
Blog

Evo Agentic AppSec: Sicherheit, die mit der KI-Entwicklung Schritt hält

KI-gestützte Entwicklung überholt herkömmliche Security-Workflows. Snyk Agentic AppSec setzt Security-Agenten ein, die Schwachstellen kontinuierlich finden, beheben und überprüfen – während Menschen die Maßstäbe festlegen.

blog feature ai violet
Blog

VulnBump: Können KI-Agenten anfällige Abhängigkeiten beheben, ohne den Build zu beschädigen?

VulnBump testet KI-Agenten anhand von 307 realen Dependency-Upgrades. Eine Lösung zählt nur, wenn die Sicherheitslücke behoben ist und Build sowie Tests weiterhin erfolgreich sind.

Blog

Frontier-Modelle fanden die Schwachstellen. Nur der Angreifer fand die Angriffsketten.

Die statische Analyse fand die Schwachstellen, doch erst Live-Angriffstests bewiesen, wie sie sich zu Angriffen verketten lassen. Ein Vergleich von Evo COS, Claude Security und Claude Code Security.