Tehnică · 7 noiembrie 2026 · 6 min de citit
Webhook-uri explicate simplu: semnătură, repetări, securitate
Ce este un webhook, cum diferă de interogarea API și cum tratați semnătura, repetarea și dublurile. Cu curl și Node.
Un webhook este un apel în sens invers. În loc ca sistemul dumneavoastră să întrebe la câteva minute un serviciu dacă s-a întâmplat ceva, serviciul apelează o adresă la dumneavoastră de îndată ce se întâmplă. Asta economisește interogări și aduce noutățile mai repede. În schimb trebuie să puneți la dispoziție și să securizați un receptor. Acest articol explică ambele.
Webhook sau interogare API
| Interogare API (polling) | Webhook | |
|---|---|---|
| Cine pornește? | Sistemul dumneavoastră | Expeditorul |
| Când? | Conform unui program, de exemplu la oră | De îndată ce apare un eveniment |
| Efort | Cereri chiar și fără noutăți | Este necesar un receptor accesibil |
| Bun pentru | Rapoarte, sincronizare, reîncărcare | Notificare, redirecționare |
| Risc | Întârziere până la următoarea interogare | Mesajul se pierde dacă receptorul nu răspunde |
În practică, mulți le combină: webhook-ul anunță evenimentul, API-ul aduce la nevoie detaliile sau acoperă golurile după o cădere.
Cum decurge o livrare
- Apare un eveniment, de exemplu „plată primită”.
- Expeditorul trimite un POST cu JSON la adresa dumneavoastră înregistrată (HTTPS).
- Receptorul dumneavoastră verifică semnătura, reține mesajul și răspunde rapid cu un cod din zona 2xx.
- Munca propriu-zisă o face sistemul dumneavoastră după aceea.
Pasul 3 este important. Stripe recomandă în documentația webhook să returnați imediat un cod 2xx, înainte de a rula logică costisitoare care ar putea declanșa un timeout. Cine mai întâi înregistrează factura și abia apoi răspunde riscă repetări inutile.
Semnătura: cum verificați proveniența
Adresa dumneavoastră este accesibilă public. Oricine ar putea deci să-i trimită mesaje inventate. De aceea expeditorii serioși semnează fiecare mesaj cu un secret comun. Uzual este un HMAC cu SHA-256 peste conținut, adesea împreună cu o marcă de timp. GitHub descrie procedura astfel: HMAC-SHA256 peste conținut, reprezentat hexazecimal, cu antetul X-Hub-Signature-256. Comparația trebuie făcută cu o funcție cu durată constantă, pentru ca atacatorii să nu poată deduce nimic din timpii de răspuns.
Puncte importante pentru receptorul dumneavoastră:
- Folosiți conținutul brut. Calculați semnătura peste octeții primiți, nu peste JSON-ul serializat din nou. Chiar și o altă trecere la rând nou schimbă semnătura.
- Verificați marca de timp. Stripe folosește o marcă de timp în semnătură și o toleranță implicită de cinci minute, pentru a preveni atacurile prin repetare. O toleranță de 0 dezactivează verificarea.
- Păstrați secretul secret. Nu în cod, nu în depozit, ci într-o variabilă de mediu sau într-un seif.
Ghiduri open source pentru expeditori și receptori reunește inițiativa Standard Webhooks. Cum își formează concret fiecare furnizor semnătura se află în documentația lui.
Repetări și dublă livrare
Dacă receptorul dumneavoastră nu răspunde sau răspunde cu un cod de eroare, expeditorul încearcă din nou. Stripe, de exemplu, repetă potrivit propriei documentații, cu intervale crescânde, până la trei zile. Alți furnizori au alte reguli, unii dezactivează webhook-ul după prea multe eșecuri. De aici rezultă:
- Socotiți dublurile. Același eveniment poate sosi de mai multe ori, chiar dacă receptorul dumneavoastră lucrează corect. Stripe atrage expres atenția asupra acestui lucru.
- Faceți prelucrarea idempotentă. Asta înseamnă: cine prelucrează de două ori același mesaj nu produce un al doilea rezultat. Stocați identificatorul evenimentului și săriți peste ce este cunoscut.
- Nu vă bazați pe ordine. Evenimentele pot sosi inversate. În caz de îndoială, verificați starea actuală prin API.
Securitate pe ambele părți
Pentru receptori: doar HTTPS, verificați semnătura și vechimea, limitați mărimea mesajului, nu executați comenzi din conținut. Tratați conținutul ca orice intrare din exterior.
Pentru expeditori care apelează adresele clienților: cine oferă webhook-uri apelează adrese pe care alții le-au înregistrat. Este o cale clasică pentru Server-Side Request Forgery. Atacatorul înregistrează o adresă internă. Serverul o apelează din rețeaua protejată. Descrierea OWASP menționează ca ținte, de exemplu, servicii interne și adrese de metadate ale furnizorilor de cloud. Măsuri de protecție rezonabile:
- permiteți doar
https://, - rezolvați adresa la înregistrare și înainte de fiecare apel și respingeți intervalele private, locale și rezervate,
- nu urmați orbește redirecționările,
- setați limite scurte de timp.
Exemplu: trimiterea unui mesaj de test cu curl
Cu curl și openssl vă puteți testa receptorul fără expeditorul real. Scriptul trimite un mesaj semnat, așa cum l-ar trimite un expeditor cu marcă de timp:
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"
Exemplu: un receptor mic în Node.js
const http = require('http')
const crypto = require('crypto')
const GEHEIMNIS = process.env.WEBHOOK_GEHEIMNIS || 'test-geheimnis'
const gesehen = new Set() // în realitate: bază de date
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)
Receptorul verifică semnătura și vechimea, sare peste identificatorii cunoscuți și răspunde imediat. Pentru operare aveți nevoie suplimentar de HTTPS, o limită de mărime și o coadă pentru prelucrarea ulterioară.
Unde sunt răspândite webhook-urile
Serviciile de plată, platformele de cod și instrumentele de formulare raportează evenimente prin webhook. Și recunoașterea vizitatorilor poate: dacă sistemul raportează o firmă care vă vizitează pagina, aceasta ajunge direct într-un instrument la alegerea dumneavoastră. Cum duce asta într-un sistem de vânzări arată Cum aduceți vizitele de pe site în CRM. Alternativa prin interogare o descrie Ce firme vă vizitează site-ul?. Ce date circulă astfel explică Cum funcționează recunoașterea firmelor?.
Întrebări frecvente
Care este diferența dintre webhook și API? La API sistemul dumneavoastră întreabă, la webhook expeditorul se anunță singur. Ambele se pot combina.
Ce se întâmplă dacă receptorul meu cade? Majoritatea expeditorilor repetă livrarea pe o anumită perioadă. Cât timp și de câte ori se află în documentația furnizorului. Pentru goluri ar trebui să planificați o sincronizare prin API.
Este suficient să verific adresa expeditorului? Nu. Adresele expeditorilor pot fi falsificate și se schimbă. Fiabilă este doar semnătura cu un secret pe care îl cunoașteți doar dumneavoastră și expeditorul.