Przejdź do treści

Technika · 7 listopada 2026 · 5 min czytania

Webhooki w prostych słowach: podpis, ponowienia, bezpieczeństwo

Czym jest webhook, czym różni się od zapytania API i jak obsłużyć podpis, ponowienia i duplikaty. Z przykładami w curl i Node.

Dwie drogi w porównaniu: w zapytaniu API Państwa system pyta wciąż od nowa, w webhooku nadawca jednorazowo wywołuje Państwa adres, gdy coś się wydarzy
Grafika: Whovisit · Zobacz grafikę w pełnym rozmiarze

Webhook to telefon w przeciwną stronę. Zamiast że Państwa system co kilka minut pyta usługę, czy coś się wydarzyło, usługa wywołuje adres u Państwa, gdy tylko to się stanie. Oszczędza to zapytania i dostarcza nowości szybciej. W zamian trzeba udostępnić odbiorcę i go zabezpieczyć. Artykuł wyjaśnia jedno i drugie.

Webhook czy zapytanie API

Zapytanie API (polling)Webhook
Kto zaczyna?Państwa systemNadawca
Kiedy?Według harmonogramu, np. co godzinęGdy tylko zajdzie zdarzenie
NakładZapytania także bez nowościPotrzebny osiągalny odbiorca
Dobre doRaportów, uzgodnień, doładowywaniaPowiadomień, przekazywania dalej
RyzykoOpóźnienie do następnego zapytaniaWiadomość ginie, jeśli odbiorca nie odpowiada

W praktyce wiele osób łączy jedno i drugie: webhook zgłasza zdarzenie, API pobiera w razie potrzeby szczegóły lub wypełnia luki po awarii.

Jak przebiega doręczenie

  1. Zachodzi zdarzenie, na przykład „wpłynęła płatność”.
  2. Nadawca wysyła POST z JSON na zapisany adres (HTTPS).
  3. Państwa odbiorca sprawdza podpis, zapamiętuje wiadomość i szybko odpowiada kodem z zakresu 2xx.
  4. Właściwą pracę wykonuje potem Państwa system.

Krok 3 jest ważny. Stripe zaleca w dokumentacji webhooków, aby natychmiast zwrócić kod 2xx, zanim zacznie się kosztowna logika, która mogłaby wywołać przekroczenie czasu. Kto najpierw księguje fakturę, a dopiero potem odpowiada, ryzykuje niepotrzebne ponowienia.

Podpis: jak sprawdzić pochodzenie

Państwa adres jest publicznie osiągalny. Każdy mógłby więc wysyłać na niego zmyślone wiadomości. Dlatego rzetelni nadawcy podpisują każdą wiadomość wspólnym sekretem. Zwyczajowo jest to HMAC z SHA-256 z treści, często razem ze znacznikiem czasu. GitHub opisuje tę procedurę tak: HMAC-SHA256 z treści, przedstawiony szesnastkowo, z nagłówkiem X-Hub-Signature-256. Porównanie powinno odbywać się funkcją o stałym czasie działania, aby napastnicy nie mogli niczego wyprowadzić z czasów odpowiedzi.

Ważne punkty dla Państwa odbiorcy:

  • Używać treści surowej. Podpis należy obliczać z otrzymanych bajtów, a nie z ponownie zserializowanego JSON. Już inny podział wiersza zmienia podpis.
  • Sprawdzać znacznik czasu. Stripe stosuje znacznik czasu w podpisie i domyślną tolerancję pięciu minut, aby zapobiegać atakom przez powtórzenie. Tolerancja 0 wyłącza kontrolę.
  • Utrzymywać sekret w tajemnicy. Nie w kodzie, nie w repozytorium, lecz w zmiennej środowiskowej lub skarbcu.

Wytyczne open source dla nadawców i odbiorców zbiera inicjatywa Standard Webhooks. Jak każdy dostawca tworzy swój podpis konkretnie, opisuje jego dokumentacja.

Ponowienia i podwójne doręczenie

Jeśli Państwa odbiorca nie odpowiada lub odpowiada kodem błędu, nadawca próbuje ponownie. Stripe na przykład ponawia według własnej dokumentacji ze wzrastającymi odstępami do trzech dni. Inni dostawcy mają inne zasady, niektórzy wyłączają webhook po zbyt wielu niepowodzeniach. Wynika z tego:

  • Należy liczyć się z duplikatami. To samo zdarzenie może przyjść wielokrotnie, nawet jeśli Państwa odbiorca działa prawidłowo. Stripe wyraźnie na to wskazuje.
  • Przetwarzanie powinno być idempotentne. Oznacza to: kto przetworzy tę samą wiadomość dwa razy, nie wytwarza drugiego wyniku. Należy zapisać identyfikator zdarzenia i pomijać znane.
  • Nie polegać na kolejności. Zdarzenia mogą przychodzić w zamienionej kolejności. W razie wątpliwości należy sprawdzić aktualny stan przez API.

Bezpieczeństwo po obu stronach

Dla odbiorców: tylko HTTPS, sprawdzać podpis i wiek, ograniczać rozmiar wiadomości, nie wykonywać poleceń z treści. Treść należy traktować jak każde dane wejściowe z zewnątrz.

Dla nadawców, którzy wywołują adresy klientów: kto oferuje webhooki, wywołuje adresy wpisane przez innych. To klasyczna droga dla Server-Side Request Forgery. Napastnik wpisuje adres wewnętrzny. Serwer wywołuje go z chronionej sieci. Opis OWASP wymienia jako cele na przykład usługi wewnętrzne i adresy metadanych dostawców chmury. Sensowne środki ochrony:

  • dopuszczać tylko https://,
  • rozwiązywać adres przy wpisywaniu i przed każdym wywołaniem oraz odrzucać zakresy prywatne, lokalne i zarezerwowane,
  • nie podążać ślepo za przekierowaniami,
  • ustawiać krótkie limity czasu.

Przykład: wysłanie wiadomości testowej za pomocą curl

Za pomocą curl i openssl mogą Państwo przetestować swojego odbiorcę bez prawdziwego nadawcy. Skrypt wysyła podpisaną wiadomość, taką jak wysłałby nadawca ze znacznikiem czasu:

GEHEIMNIS="test-geheimnis"
ZEIT=$(date +%s)
INHALT='{"id":"evt_1","typ":"besuch.begonnen","firma":"beispiel-firma.example"}'
SIGNATUR="sha256=$(printf '%s.%s' "$ZEIT" "$INHALT" | openssl dgst -sha256 -hmac "$GEHEIMNIS" | sed 's/^.* //')"

curl -X POST http://localhost:3000/webhook \
  -H "Content-Type: application/json" \
  -H "X-Zeit: $ZEIT" \
  -H "X-Signatur: $SIGNATUR" \
  -d "$INHALT"

Przykład: mały odbiorca w Node.js

const http = require('http')
const crypto = require('crypto')
const GEHEIMNIS = process.env.WEBHOOK_GEHEIMNIS || 'test-geheimnis'
const gesehen = new Set() // w praktyce: baza danych

http.createServer((req, res) => {
  if (req.method !== 'POST' || req.url !== '/webhook') { res.writeHead(404); return res.end() }
  const teile = []
  req.on('data', t => teile.push(t))
  req.on('end', () => {
    const roh = Buffer.concat(teile).toString('utf8')
    const zeit = req.headers['x-zeit'] || ''
    const erwartet = 'sha256=' + crypto.createHmac('sha256', GEHEIMNIS).update(zeit + '.' + roh).digest('hex')
    const erhalten = String(req.headers['x-signatur'] || '')
    const a = Buffer.from(erwartet), b = Buffer.from(erhalten)
    const echt = a.length === b.length && crypto.timingSafeEqual(a, b)
    const frisch = Math.abs(Date.now() / 1000 - Number(zeit)) < 300
    if (!echt || !frisch) { res.writeHead(401); return res.end() }
    const ereignis = JSON.parse(roh)
    if (!gesehen.has(ereignis.id)) { gesehen.add(ereignis.id); console.log('neu:', ereignis.typ) }
    res.writeHead(200); res.end('ok')
  })
}).listen(3000)

Odbiorca sprawdza podpis i wiek, pomija znane identyfikatory i odpowiada natychmiast. Do działania produkcyjnego potrzebują Państwo dodatkowo HTTPS, ograniczenia rozmiaru i kolejki do dalszego przetwarzania.

Gdzie webhooki są rozpowszechnione

Usługi płatnicze, platformy kodu i narzędzia do formularzy zgłaszają zdarzenia przez webhook. Rozpoznawanie odwiedzających także to potrafi: gdy system zgłasza firmę odwiedzającą Państwa stronę, trafia ona bezpośrednio do wybranego narzędzia. Jak to prowadzi do systemu sprzedaży, pokazuje artykuł Wizyty na stronie w CRM: 4 drogi w porównaniu. Alternatywę przez zapytanie opisuje artykuł Jakie firmy odwiedzają moją stronę?. Jakie dane przy tym płyną, wyjaśnia artykuł Jak działa rozpoznawanie firm?.

Najczęstsze pytania

Jaka jest różnica między webhookiem a API? W API Państwa system pyta, w webhooku nadawca zgłasza się sam. Jedno i drugie można łączyć.

Co się dzieje, gdy mój odbiorca zawiedzie? Większość nadawców ponawia doręczenie przez pewien czas. Jak długo i jak często, podaje dokumentacja dostawcy. Na luki należy zaplanować uzgodnienie przez API.

Czy wystarczy sprawdzić adres nadawcy? Nie. Adresy nadawców można podrobić i zmieniają się. Wiarygodny jest tylko podpis z sekretem, który znają wyłącznie Państwo i nadawca.

Czytaj dalej