Bezpieczeństwo i dostarczanie webhooków
Weryfikacja podpisów webhooków, mechanizm ponownych prób dostarczenia oraz rozwiązywanie problemów.
Dlaczego to ważne
Weryfikacja podpisów potwierdza, że dane są autentyczne i nie zostały zmodyfikowane. Zrozumienie mechanizmu ponownych prób i automatycznego wyłączania pomaga budować odporne integracje.
Weryfikacja podpisów
Każde dostarczenie jest podpisywane za pomocą klucza podpisującego przypisanego do endpointu przy użyciu HMAC-SHA256. Podpis obejmuje znacznik czasu oraz treść żądania. Ochrona przed atakami powtórkowymi (replay attacks) wymaga też sprawdzenia wieku żądania, opisanego poniżej.
Podpisywana treść to znacznik czasu w formacie ISO 8601 i treść JSON połączone kropką:
<timestamp>.<body>
Ruby
timestamp = request.headers["X-Webhook-Timestamp"]
signature = request.headers["X-Webhook-Signature"]
body = request.body.read
expected = OpenSSL::HMAC.hexdigest("SHA256", signing_secret, "#{timestamp}.#{body}")
if ActiveSupport::SecurityUtils.secure_compare(expected, signature)
# Signature is valid
else
head :unauthorized
end
Node.js
const crypto = require("crypto");
function verifySignature(signingSecret, timestamp, body, signature) {
const expected = crypto
.createHmac("sha256", signingSecret)
.update(`${timestamp}.${body}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
Python
import hmac
import hashlib
def verify_signature(signing_secret, timestamp, body, signature):
expected = hmac.new(
signing_secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Zapobieganie atakom powtórkowym
Porównaj wartość X-Webhook-Timestamp z bieżącym czasem i odrzucaj żądania starsze niż 5 minut.
Rotacja klucza podpisującego
Kliknij Zmień przy kluczu podpisującym na stronie szczegółów webhooka, żeby wygenerować nowy klucz podpisujący. Poprzedni klucz zostaje natychmiast unieważniony.
Nowy klucz poznasz dopiero po rotacji. Przygotuj możliwość szybkiej zmiany klucza w endpoincie:
- Wykonaj rotację klucza w Kit
- Skopiuj nowy klucz do konfiguracji endpointu
- Wyślij test i sprawdź, czy podpis jest poprawnie weryfikowany
- Usuń stary klucz z konfiguracji endpointu, jeśli nadal go przechowujesz
Między rotacją a aktualizacją endpoint może odrzucać dostarczenia. Ta procedura nie zapewnia zmiany bez przestoju.
Dostarczanie i ponowne próby
Kit wysyła ładunek JSON metodą POST do twojego endpointu z następującymi limitami czasu:
| Ustawienie | Wartość |
|---|---|
| Limit czasu połączenia | 10 sekund |
| Limit czasu odczytu | 15 sekund |
Odpowiedź 2xx oznacza dostarczenie jako zakończone. Wszystko inne to błąd, a sposób jego obsługi przez Kit zależy od tego, czy błąd jest przejściowy, czy trwały.
flowchart TD
post["Wysłanie ładunku POST do endpointu"] --> ok{"Odpowiedź 2xx?"}
ok -- "Tak" --> completed["Dostarczenie zakończone"]
ok -- "Nie" --> kind{"Błąd przejściowy? (5xx, 408, 429 lub błąd sieci)"}
kind -- "Nie: dowolny inny 4xx (trwały)" --> errored["Dostarczenie z błędem, bez ponawiania"]
kind -- "Tak" --> attempts{"Mniej niż 5 prób?"}
attempts -- "Tak" --> backoff["Oczekiwanie z wielomianowym opóźnieniem"]
backoff --> post
attempts -- "Nie: próby wyczerpane" --> errored
errored --> threshold{"15 kolejnych nieudanych dostarczeń?"}
threshold -- "Tak" --> disabled["Webhook automatycznie wyłączony"]
threshold -- "Nie" --> nextevent["Oczekiwanie na następne zdarzenie"]
disabled -- "Resume: licznik błędów zresetowany" --> active["Webhook znów aktywny"]
Błędy przejściowe (z ponawianiem)
Błędy przejściowe to problemy, które prawdopodobnie same ustąpią: twój endpoint był chwilowo przeciążony, przekroczył limit czasu albo był nieosiągalny. Kit ponawia je do 5 prób z wielomianowym opóźnieniem (każde oczekiwanie dłuższe od poprzedniego).
Błąd jest traktowany jako przejściowy, gdy odpowiedź to:
- HTTP od
500do599(błędy serwera) - HTTP
408(Request Timeout) - HTTP
429(Too Many Requests) - Błąd sieci lub przekroczenie limitu czasu (odrzucone połączenie, błąd DNS, timeout odczytu)
| Próba | Zachowanie |
|---|---|
| 1 | Natychmiastowa |
| 2–5 | Wielomianowo rosnące czasy oczekiwania |
Błędy trwałe (bez ponawiania)
Błędy trwałe oznaczają, że samo żądanie jest nie do przyjęcia dla twojego endpointu i ponawianie nic nie da. Kit od razu oznacza dostarczenie jako błędne i go nie ponawia.
Błąd jest traktowany jako trwały, gdy odpowiedź to dowolny inny status 4xx, na przykład 400, 401, 404 lub 410. Jeśli twój endpoint zwraca taki kod przez pomyłkę, popraw trasę lub uwierzytelnianie po swojej stronie; Kit podejmie nową próbę dostarczenia przy następnym zdarzeniu, ale nie ponowi tego błędnego dostarczenia.
Automatyczne wyłączanie
Po 15 kolejnych nieudanych dostarczeniach (niezależnie od typu zdarzenia) Kit automatycznie wyłącza webhooka.
Po wyłączeniu:
- Dalsze próby dostarczenia nie są podejmowane
- Endpoint pokazuje czerwoną plakietkę Wyłączony wraz z informacją, że został odcięty po zbyt wielu kolejnych niepowodzeniach
Kliknięcie Wznów ponownie aktywuje webhooka. Powoduje to zresetowanie licznika błędów i przywrócenie webhooka do stanu aktywnego.
Logi dostarczania
Każdy endpoint webhooka ma zakładkę Dostarczenia pokazującą 50 ostatnich dostarczeń. Każdy wpis zawiera:
| Pole | Opis |
|---|---|
| Zdarzenie | Typ zdarzenia, które wywołało dostarczenie |
| Status | Oczekujący, Dostarczono lub Niepowodzenie |
| Liczba prób | Liczba prób dostarczenia |
| Nagłówki żądania | Nagłówki wysłane wraz z żądaniem |
| Odpowiedź | Kod statusu HTTP lub komunikat błędu |
| Znacznik czasu | Data i godzina utworzenia dostarczenia |
Statusy dostarczenia
| Status | Znaczenie |
|---|---|
| Oczekujący | W kolejce lub w trakcie wysyłki. Oba przypadki mają tę samą plakietkę, nie ma więc osobnego statusu dla dostarczeń w toku |
| Dostarczono | Endpoint zwrócił odpowiedź 2xx
|
| Niepowodzenie | Wszystkie ponowne próby wyczerpane lub endpoint zwrócił błąd trwały (4xx) |
Przechowywanie danych
Logi dostarczania są przechowywane przez 30 dni, a następnie automatycznie usuwane. Jeśli potrzebujesz dłuższego przechowywania, zapisuj ładunki po swojej stronie.
Wymagania dotyczące adresów URL
- Wymagany protokół HTTPS: endpointy HTTP są odrzucane
- Tylko publiczne adresy IP: adresy URL prowadzące do adresów prywatnych (localhost, 10.x.x.x, 192.168.x.x itp.) są blokowane
- Adresy URL są walidowane podczas tworzenia oraz przed każdym dostarczeniem
Rozwiązywanie problemów
| Problem | Przyczyna | Rozwiązanie |
|---|---|---|
| Niezgodność podpisu | Nieprawidłowy klucz podpisujący lub treść zmodyfikowana przed weryfikacją | Zweryfikuj podpis na podstawie surowej treści żądania z aktualnym kluczem |
| Przekroczenie limitu czasu dostarczenia | Endpoint potrzebuje więcej niż 15 sekund | Zwróć 200 natychmiast i przetwórz dane asynchronicznie |
| Dostarczenie z błędem bez ponowienia | Endpoint zwrócił trwały 4xx (np. 404, 410) |
Popraw trasę, uwierzytelnianie lub metodę; ponawiane są tylko błędy przejściowe (5xx, 408, 429, sieć/timeout) |
| Webhook automatycznie wyłączony | 15 kolejnych niepowodzeń | Napraw endpoint, a następnie kliknij Wznów |
| Adres URL odrzucony | Prywatny adres IP lub protokół HTTP | Użyj adresu HTTPS prowadzącego do publicznego adresu IP |
| Zdarzenia nie są wyzwalane | Webhook wstrzymany lub typ zdarzenia nie jest subskrybowany | Sprawdź status webhooka i zasubskrybowane zdarzenia |
W skrócie
- Skopiuj klucz podpisujący ze strony szczegółów webhooka
- Zaimplementuj weryfikację podpisu HMAC-SHA256 w endpoincie
- Odrzucaj żądania ze znacznikami czasu starszymi niż 5 minut
-
Zwracaj kod statusu
2xxw ciągu 15 sekund - Obsługuj ponowne próby w sposób idempotentny (to samo zdarzenie może zostać dostarczone więcej niż raz)
- Monitoruj logi dostarczania pod kątem błędów
- Skonfiguruj alerty na wypadek automatycznego wyłączenia webhooka