Przejdź do głównej zawartości

Webhooks

REST API PhishSpot (Rozdział 27) pozwala pobierać dane na żądanie. Webhooks odwracają kierunek: zamiast Ty pollujesz nas, my wywołujemy POST do Twojego URL-a w momencie, gdy coś się dzieje. Wpięty w SIEM webhook zamienia zdarzenie opened kampanii w alert bezpieczeństwa parę sekund po kliknięciu użytkownika. Wpięty w LMS — aktualizuje rekord uczącego się bez nocnej synchronizacji.

Ten rozdział opisuje dostępne zdarzenia, jak zarejestrować endpoint, co przychodzi na kablu i jak działają retry oraz podpisywanie.

Polling to pytanie „jest coś nowego?” w pętli — i ignorowanie odpowiedzi większość czasu. Marnuje wywołania API, ma wbudowane opóźnienie (dowiadujesz się o zdarzeniach przy następnym pollu, nie w momencie ich wystąpienia), źle się skaluje (dłuższe polly tracą zdarzenia, krótsze obciążają API).

Webhooks odwracają to. Rejestrujesz URL raz; my dostarczamy każde zdarzenie pod ten adres dokładnie wtedy, gdy wystąpi. Te same dane, niższe opóźnienie, mniej requestów. Minus: musisz mieć osiągalny endpoint HTTPS, żeby odbierać dostawy — ale dla celu integracji (SIEM, SOAR, LMS, bot, narzędzie wewnętrzne) to zwykle trywialne.

Lista webhooków pokazująca trzy skonfigurowane endpointy

  1. Otwórz Ustawienia konta → Webhooks. Strona endpoints listuje istniejące webhooki z kolumnami Nazwa, URL, Zdarzenia (liczba subskrybowanych typów), Status (Włączony / Wyłączony), Dostawy (łącznie + liczba nieudanych) i Akcje.
  2. Kliknij Nowy webhook. Formularz ma cztery pola:
    • Nazwa — przyjazna etykieta. Przykłady z działającego setupu: „SIEM Splunk — security events”, „LMS Bridge — training sync”, „Slack Notify — #security channel”.
    • URL webhooka — gdzie POST-ujemy dostawy. Musi być HTTPS. Platforma odrzuca URL-e wskazujące na localhost, link-local (169.254/16) i każdy zakres prywatny RFC1918 (10/8, 172.16/12, 192.168/16). Blokuje też *.phishspot.com. Cel: webhook nie może być nadużyty do sondowania sieci wewnętrznej.
    • Subskrybuj zdarzenia — checkboxy dla każdego typu (patrz §26.3). Zaznacz co najmniej jedno.
    • Włącz endpoint webhooka — przełącznik. Wyłączony oznacza, że trzymamy rekord, ale nic nie wysyłamy.
  3. Zapisz. PhishSpot generuje klucz podpisu (64-znakowy hex z SecureRandom.hex(32)) i wyświetla go w całości na stronie szczegółów endpointu, z przyciskiem kopiowania. Zapisz go w bezpiecznym miejscu; nigdzie indziej go nie pokazujemy ponownie.

Endpoint jest aktywny natychmiast. Każde subskrybowane zdarzenie, które wystąpi od teraz, zostanie wysłane POST-em pod Twój URL.

Dziewięć typów zdarzeń jest dostępnych dziś, pogrupowanych po subiekcie:

Typ zdarzeniaKiedy się odpala
campaign.createdPowstaje nowa kampania (ręcznie albo z iteracji autopilotu).
campaign.updatedZmienia się stan kampanii, odbiorcy albo treść.
campaign.deletedKampania zostaje usunięta.
contact.createdDodano kontakt (CSV, ręcznie albo synchronizacja katalogu).
contact.updatedZmienia się email, dział, stanowisko, członkostwo w grupie albo external state.
contact.deletedKontakt zostaje usunięty z konta.
deliverable.createdWysyłka kampanii produkuje rekord deliverable (jeden per odbiorca).
deliverable.updatedStan odbiorcy się zmienia (sent → opened → clicked → submitted → educated, albo bounced).
spam_whitelist.updatedLista IP / domen nadawcy dla konta się zmienia — patrz Rozdział 22 §22.5.

Endpoint może subskrybować dowolną kombinację. Typowy SIEM subskrybuje contact.* i deliverable.*, żeby widzieć kogo atakujemy i jak reagują. Typowy bridge do LMS subskrybuje tylko deliverable.updated, bo interesuje go jedynie postęp w szkoleniach.

Każda dostawa to HTTP POST z ciałem JSON. Ciało to zdarzenie — ten sam rekord, który możesz pobrać przez API. Kształt:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "contact.created",
"created_at": "2026-05-20T14:22:33.000Z",
"api_version": 1,
"data": {
"id": 42,
"email": "anna.kowalska@cydefen.pl"
}
}
  • id — UUID identyfikujący to zdarzenie; klucz idempotentności. Powtórki tego samego zdarzenia używają tego samego id.
  • type — nazwa typu zdarzenia (jeden z dziewięciu w §26.3).
  • created_at — ISO-8601 UTC, kiedy zdarzenie wystąpiło.
  • api_version — wersja schematu jako integer. 1 dla powyższego formatu. Przyszłe zmiany łamiące kształt podniosą tę wartość, a my powiadomimy z wyprzedzeniem.
  • data — pola specyficzne dla subiektu. Na razie data zawiera id subiektu i kluczowe atrybuty identyfikujące; pełen rekord pobierzesz z REST API używając id.

Podpisywanie. Każdy POST niesie nagłówek X-Webhook-Signature zawierający HMAC-SHA256 ciała JSON, wyliczone z kluczem podpisu endpointu. Weryfikacja po Twojej stronie:

expected = OpenSSL::HMAC.hexdigest("SHA256", signing_secret, request.raw_post)
signature = request.headers["X-Webhook-Signature"]
ActiveSupport::SecurityUtils.secure_compare(expected, signature) or render status: 401
import hmac, hashlib
expected = hmac.new(secret.encode(), request.body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers["X-Webhook-Signature"]):
abort(401)

Akceptuj tylko POST-y podpisane sekretem — nigdy nie ufaj niepodpisanemu requestowi, który twierdzi, że jest z PhishSpot.

Jeśli Twój endpoint odpowie czymś innym niż HTTP 2xx, dostawa trafia do kolejki retry. Retry idzie według ustalonego harmonogramu:

PróbaOpóźnienie od poprzedniej
1(natychmiast)
2+15 sekund
3+1 minuta
4+5 minut
5+15 minut
6+1 godzina

Po 5 retry (łącznie 6 prób) dostawa jest oznaczana Failed i już nie retrowana. Licznik consecutive_failures endpointu rośnie przy każdej całkowicie nieudanej dostawie. Po przekroczeniu 5 kolejnych niepowodzeń admini konta dostają mail jeden raz (z 7-dniowym cooldownem, żeby nie spamować, gdy endpoint jest źle skonfigurowany od dawna). Sam endpoint zostaje włączony — nie wyłączamy go automatycznie, bo większość awarii jest przejściowa i samonaprawiające się dostawy powinny móc się udać.

Jeśli chcesz ręcznie ponowić jedną nieudaną dostawę po naprawie po swojej stronie, otwórz stronę szczegółów dostawy (§26.6) i kliknij Ponów. To tworzy świeżą dostawę dla tego samego zdarzenia, z wyzerowanymi licznikami prób.

Strona szczegółów endpointu (kliknij dowolną nazwę endpointu na liście) pokazuje wszystko, co wiemy o tym endpoincie:

  • Szczegóły endpointu — nazwa, URL, wersja API, klucz podpisu (z przyciskiem kopiowania), subskrybowane zdarzenia.
  • Status — przełącznik Włączony / Wyłączony.
  • Ostatnie dostawy — tabela ostatnich 50 dostaw z kolumnami:
    • Typ zdarzenia (np. contact.created)
    • Status — Oczekuje / Dostarcza / Dostarczone / Nieudane
    • Próby — aktualna liczba prób / max (np. 1 / 5, 5 / 5)
    • Ostatnia próba — znacznik czasu
    • Akcje — Zobacz szczegóły, Ponów dostawę

Kliknij Zobacz szczegóły na dowolnym wierszu dostawy, aby otworzyć stronę szczegółów. Pokazuje pełny payload (sformatowany JSON), docelowy URL i log per-próba: numer próby, kod HTTP, czas odpowiedzi w ms i ciało odpowiedzi (albo komunikat błędu, jeśli request nie doszedł). To Twoja główna powierzchnia debugowania, gdy integracja się psuje.

Szczegóły endpointu webhook — klucz podpisu, subskrybowane zdarzenia, ostatnie dostawy z licznikami prób

Szczegóły dostawy webhooka — payload zdarzenia + log per-próba HTTP

  • Potwierdzaj szybko. Twój handler powinien przyjąć dostawę (zwrócić 2xx) i przetwarzać asynchronicznie. My się poddajemy wolnym responderom — a wolni responderzy kaskadują się w retry, które kaskadują się w maile o „consecutive failures”.
  • Obsłuż duplikaty. Problemy sieciowe mogą sprawić, że to samo zdarzenie dotrze dwa razy. Deduplikuj po polu id — jest stabilne między retry.
  • Weryfikuj podpis. Nie działaj na webhooku, którego podpis się nie zgadza. POST chroniony sekretem to jedyne uwierzytelnienie; bez niego integrację może odtworzyć każdy, kto zgadnie URL.
  • Spodziewaj się szczytów. Kampania z 1000 odbiorców produkuje 1000 zdarzeń deliverable.created w krótkim oknie. Upewnij się, że Twój handler się skaluje.
  • Rotuj sekret jeśli wycieknie. Usuń i utwórz endpoint na nowo — UI nie oferuje dziś rotacji „w miejscu”.