# Webhooky jednoduše: podpis, opakování, bezpečnost

> Co je webhook, jak se liší od dotazu na API a jak zacházet s podpisem, opakováním a duplicitami. S curl a Node.

Quelle: https://whovisit.app/cs/blog/webhooky-jednoduse-podpis-opakovani
Veröffentlicht: 2026-11-07
Herausgeber: Whovisit (ARAVILO, Innsbruck)

Webhook je telefonát opačným směrem. Místo aby se váš systém u služby každých pár minut ptal, zda se něco stalo, služba zavolá adresu u vás, jakmile se to stane. Šetří to dotazy a dodává novinky rychleji. Musíte ale zajistit a zabezpečit příjemce. Tento článek vysvětluje obojí.

## Webhook, nebo dotaz na API

| | Dotaz na API (polling) | Webhook |
|---|---|---|
| Kdo začíná? | Váš systém | Odesílatel |
| Kdy? | Podle plánu, například každou hodinu | Jakmile nastane událost |
| Náročnost | Dotazy i bez novinek | Nutný dosažitelný příjemce |
| Dobré pro | Reporty, srovnání, doplnění | Upozornění, přeposílání |
| Riziko | Zpoždění do dalšího dotazu | Zpráva se ztratí, pokud příjemce neodpoví |

V praxi mnozí kombinují obojí: Webhook oznámí událost, API podle potřeby stáhne podrobnosti nebo vyplní mezery po výpadku.

## Jak probíhá doručení

1. Nastane událost, například „platba přijata“.
2. Odesílatel pošle POST s JSON na vaši uloženou adresu (HTTPS).
3. Váš příjemce ověří podpis, zprávu si poznamená a rychle odpoví kódem z rozsahu 2xx.
4. Vlastní práci poté vykoná váš systém.

Krok 3 je důležitý. Stripe v [dokumentaci webhooků](https://docs.stripe.com/webhooks) doporučuje okamžitě vrátit kód 2xx, než se spustí náročná logika, která by mohla vyvolat timeout. Kdo nejdřív zaúčtuje fakturu a teprve pak odpoví, riskuje zbytečná opakování.

## Podpis: jak ověřit původ

Vaše adresa je veřejně dosažitelná. Kdokoli by na ni tedy mohl posílat vymyšlené zprávy. Proto seriózní odesílatelé každou zprávu podepisují společným tajemstvím. Obvyklý je HMAC se SHA-256 přes obsah, často spolu s časovým razítkem. [GitHub tento postup popisuje](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries) takto: HMAC-SHA256 přes obsah, vyjádřený hexadecimálně, s hlavičkou `X-Hub-Signature-256`. Porovnání má probíhat funkcí s konstantní dobou běhu, aby útočníci nemohli z dob odpovědi nic odvodit.

Důležité body pro vašeho příjemce:

- **Použít surový obsah.** Podpis počítejte přes přijaté bajty, nikoli přes znovu serializovaný JSON. Už jiný zlom řádku změní podpis.
- **Ověřit časové razítko.** Stripe používá časové razítko v podpisu a výchozí toleranci pět minut, aby zabránil útokům opakováním. Tolerance 0 kontrolu vypíná.
- **Udržet tajemství v tajnosti.** Ne v kódu, ne v repozitáři, ale v proměnné prostředí nebo v trezoru.

Zásady open source pro odesílatele a příjemce shrnuje iniciativa [Standard Webhooks](https://www.standardwebhooks.com/). Jak konkrétně každý poskytovatel tvoří svůj podpis, je v jeho dokumentaci.

## Opakování a dvojí doručení

Pokud váš příjemce neodpoví nebo odpoví chybovým kódem, odesílatel to zkusí znovu. Stripe podle vlastní dokumentace opakuje s rostoucími odstupy až tři dny. Jiní poskytovatelé mají jiná pravidla, někteří webhook po příliš mnoha nezdařených pokusech vypnou. Z toho plyne:

- **Počítejte s duplicitami.** Táž událost může dorazit vícekrát, i když váš příjemce pracuje správně. Stripe na to výslovně upozorňuje.
- **Zpracování udělejte idempotentní.** To znamená: Kdo zpracuje tutéž zprávu dvakrát, nevytvoří druhý výsledek. Uložte identifikátor události a známé přeskakujte.
- **Nespoléhejte na pořadí.** Události mohou přijít prohozené. V případě pochybností ověřte aktuální stav přes API.

## Bezpečnost na obou stranách

**Pro příjemce:** jen HTTPS, kontrolovat podpis a stáří, omezit velikost zprávy, neprovádět příkazy z obsahu. S obsahem zacházejte jako s každým vstupem zvenčí.

**Pro odesílatele, kteří volají adresy zákazníků:** Kdo nabízí webhooky, volá adresy, které zapsali jiní. To je klasická cesta pro Server-Side Request Forgery. Útočník zapíše interní adresu. Server ji zavolá z chráněné sítě. [Popis OWASP](https://community.owasp.org/attacks/Server_Side_Request_Forgery) uvádí jako cíle například interní služby a adresy metadat cloudových poskytovatelů. Rozumná ochranná opatření:

- povolit jen `https://`,
- adresu při zápisu i před každým voláním přeložit a odmítnout soukromé, lokální a rezervované rozsahy,
- nesledovat přesměrování slepě,
- nastavit krátké časové limity.

## Příklad: odeslat zkušební zprávu pomocí curl

Pomocí curl a openssl můžete svého příjemce otestovat bez skutečného odesílatele. Skript odešle podepsanou zprávu, jakou by poslal odesílatel s časovým razítkem:

```bash
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"
```

## Příklad: malý příjemce v Node.js

```js
const http = require('http')
const crypto = require('crypto')
const GEHEIMNIS = process.env.WEBHOOK_GEHEIMNIS || 'test-geheimnis'
const gesehen = new Set() // ve skutečnosti: databáze

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

Příjemce ověřuje podpis a stáří, přeskakuje známé identifikátory a odpovídá okamžitě. Pro provoz potřebujete navíc HTTPS, omezení velikosti a frontu pro další zpracování.

## Kde jsou webhooky rozšířené

Platební služby, platformy pro kód a nástroje pro formuláře hlásí události webhookem. Také rozpoznání návštěvníků to umí: Pokud systém ohlásí firmu, která navštívila vaši stránku, přistane přímo v nástroji podle vaší volby. Jak to vede do obchodního systému, ukazuje [Jak dostat návštěvy webu do CRM](https://whovisit.app/blog/website-besuche-ins-crm). Alternativu dotazem popisuje [Které firmy navštěvují můj web?](https://whovisit.app/blog/welche-firmen-besuchen-meine-website). Jaká data přitom tečou, vysvětluje [Jak funguje rozpoznání firem?](https://whovisit.app/blog/firmenerkennung-website-technik).

## Časté otázky

**Jaký je rozdíl mezi webhookem a API?** U API se ptá váš systém, u webhooku se odesílatel ozve sám. Obojí lze kombinovat.

**Co se stane, když můj příjemce vypadne?** Většina odesílatelů doručení po určitou dobu opakuje. Jak dlouho a jak často, je v dokumentaci poskytovatele. Pro mezery byste měli naplánovat srovnání přes API.

**Stačí kontrolovat adresu odesílatele?** Ne. Adresy odesílatelů lze předstírat a mění se. Spolehlivý je jen podpis s tajemstvím, které znáte jen vy a odesílatel.
