Configuração e segurança
1. Validação HMAC
Todo webhook é assinado com HMAC-SHA256 usando o signing_secret retornado no registro (Configurando webhooks). A assinatura vai no header X-IORQ-Signature:
X-IORQ-Signature: t=1715601234,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdFormato: t=<timestamp>,v1=<hash>. O timestamp também aparece em X-IORQ-Timestamp (redundante para conveniência).
Como calcular do seu lado
- Extraia o
timestampe ov1do header - Concatene:
signed_payload = timestamp + "." + body_raw - Calcule HMAC-SHA256(
signing_secret,signed_payload) - Compare em tempo constante com
v1(usehmac.compare_digestem Python ou equivalente) - Rejeite se diferente, ou se o
timestamptem mais de 5 minutos de diferença do agora
Implementações
# Python
import hmac, hashlib, time
def verify(payload_bytes, signature_header, secret, tolerance=300):
parts = dict(p.split('=') for p in signature_header.split(','))
ts, sig = parts['t'], parts['v1']
if abs(time.time() - int(ts)) > tolerance:
return False
signed = f"{ts}.{payload_bytes.decode()}"
expected = hmac.new(secret.encode(), signed.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)// Node.js
const crypto = require('crypto');
function verify(payloadBytes, signatureHeader, secret, tolerance = 300) {
const parts = Object.fromEntries(
signatureHeader.split(',').map(p => p.split('='))
);
const { t: ts, v1: sig } = parts;
if (Math.abs(Date.now() / 1000 - parseInt(ts)) > tolerance) return false;
const signed = `${ts}.${payloadBytes.toString()}`;
const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}2. Defesa contra replay
Sem proteção contra replay, alguém pode capturar uma chamada legítima e reenviar depois — mesmo sem conhecer o secret. Duas defesas:
- Tolerância de tempo — recuse eventos cujo
X-IORQ-Timestampesteja mais de 5 minutos do horário do servidor. Já incluído no exemplo de verificação acima. - Deduplicação por
X-IORQ-Event-Id— guarde os IDs de eventos processados nos últimos N minutos; rejeite duplicados.
3. TLS e certificado
- A URL registrada precisa ser HTTPS com certificado válido emitido por CA pública. A IORQ verifica a cadeia — self-signed é rejeitado.
- Mínimo TLS 1.2. TLS 1.3 recomendado.
- Não use certificate pinning sem aviso prévio — a IORQ pode rotacionar certificados sem aviso conforme política de renovação.
4. IPs de origem
Para defesa em profundidade, você pode restringir seu endpoint a aceitar chamadas dos IPs de saída da IORQ. A lista é pública e estável (mudanças com 30 dias de aviso):
| Ambiente | IPs CIDR |
|---|---|
| Sandbox | 52.67.0.0/24 |
| Produção | 54.232.0.0/24 |
CuidadoRestrição por IP não substitui validação HMAC. Trate como segunda barreira, não primeira. A validação HMAC é a defesa autoritativa.
5. Rotação do signing_secret
signing_secretEm caso de suspeita de vazamento, rotacione imediatamente:
curl -X POST 'https://hs-api.iorq.com.br/webhook/wh_2A8f1b3c4d5e/rotate-secret' \
-H 'Authorization: Bearer YOUR_TOKEN'{
"webhook_id": "wh_2A8f1b3c4d5e",
"signing_secret": "whsec_NEW_SECRET",
"rotated_at": "2026-05-13T12:30:00Z",
"old_secret_valid_until": "2026-05-13T13:00:00Z"
}O secret antigo continua válido por 30 minutos após a rotação, para você atualizar seus serviços sem janela de invalidação. Após esse prazo, apenas o novo aceita.
6. O que responder no webhook
| HTTP | Quando devolver | Comportamento IORQ |
|---|---|---|
200, 201, 204 | Recebido e enfileirado/processado | Marca como entregue, não reenvia |
4xx | Erro de assinatura, evento desconhecido, payload corrompido | Marca como falha permanente; não reenvia |
5xx | Erro temporário do seu lado | Reenvia com backoff exponencial (ver Idempotência) |
| Timeout (acima de 10s) | Não respondeu a tempo | Reenvia como se fosse 5xx |
7. Próximos passos
Updated about 5 hours ago