Pular para o conteúdo

Verificar assinatura

Toda entrega de webhook leva o cabeçalho:

X-LiteZap-Signature: t=1792252800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t: momento da assinatura (Unix, segundos).
  • v1: HMAC-SHA256(secret, "<t>.<corpo>") em hexadecimal, onde secret é o segredo do webhook e <corpo> é o corpo bruto da requisição.
  1. Leia o corpo bruto, antes de qualquer parse de JSON (reserializar muda os bytes e quebra a assinatura).
  2. Separe t e v1 do cabeçalho.
  3. Recuse se t estiver a mais de 5 minutos do seu relógio (anti-replay).
  4. Calcule o HMAC de "<t>.<corpo>" e compare com v1 em tempo constante.
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"math"
"strconv"
"strings"
"time"
)
// verify confere X-LiteZap-Signature ("t=<unix>,v1=<hex>") com o corpo bruto.
func verify(secret, header string, body []byte) bool {
var ts string
var sigs []string
for _, p := range strings.Split(header, ",") {
k, v, _ := strings.Cut(p, "=")
switch k {
case "t":
ts = v
case "v1":
sigs = append(sigs, v)
}
}
t, err := strconv.ParseInt(ts, 10, 64)
if err != nil || math.Abs(float64(time.Now().Unix()-t)) > 300 {
return false // sem timestamp ou mais de 5 minutos de diferença (anti-replay)
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "." + string(body)))
want := hex.EncodeToString(mac.Sum(nil))
for _, s := range sigs {
if hmac.Equal([]byte(s), []byte(want)) {
return true
}
}
return false
}

O cabeçalho pode trazer mais de um v1 no futuro (rotação de segredo): aceite se qualquer um bater, como nos exemplos em Go e Python.

Janela do terminal
SECRET=whsec_exemplo_para_teste_1234
BODY='{"id":"evt_teste","type":"instance.status","version":1,"instance_id":"inst_x","timestamp":"2026-10-17T13:40:02Z","data":{"state":"connected"}}'
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -s -X POST http://127.0.0.1:9000/ -H "X-LiteZap-Signature: t=$T,v1=$SIG" -d "$BODY"