Skip to main content
Todo webhook entregue pela Repass é assinado com HMAC-SHA256, usando um segredo exclusivo do endpoint. A assinatura viaja no header Repass-Signature e permite que você confirme que o corpo veio da Repass e não foi adulterado em trânsito. Esta página descreve o formato da assinatura, como validá-la passo a passo e como rotacionar o segredo sem perder entregas.
Para o panorama de webhooks de saída (assinaturas por endpoint, fan-out e entregas), veja Webhooks: visão geral. Para reentregas e tentativas, veja Retries e dead letter.

O header Repass-Signature

Cada entrega inclui dois headers:
O header Repass-Signature é uma lista de pares chave=valor separados por vírgula:
integer
Timestamp Unix em segundos de quando a entrega foi assinada. Use-o para proteção contra replay.
string
Assinatura HMAC-SHA256 em hexadecimal. Pode aparecer mais de uma vez: durante a janela de rotação do segredo, a entrega é assinada com o segredo atual e o anterior, gerando dois v1=. Considere a assinatura válida se qualquer v1 conferir.
A assinatura é calculada sobre a string concatenada "{t}.{body}", onde body é o corpo HTTP exato (bytes brutos) recebido na requisição:
Assine o corpo bruto, antes de qualquer parsing. Se você fizer JSON.parse e depois JSON.stringify o objeto, a reserialização pode reordenar chaves ou mudar o espaçamento, e a assinatura nunca vai conferir. Capture os bytes crus do request antes de desserializar.

Passo a passo da validação

1

Recupere o segredo do endpoint

O segredo é gerado pela Repass no formato rwhs_<base64url> e devolvido em claro apenas na resposta de criação do endpoint (POST /webhook-endpoints) e na rotação (POST /webhook-endpoints/{endpointId}/rotate-secret). Em qualquer outra resposta ele aparece mascarado como rwhs_***<4 últimos>. Guarde-o de forma segura no momento em que for emitido.
2

Leia o corpo bruto e o header

Capture o corpo da requisição como string/bytes crus, sem parsing, e leia o header Repass-Signature. Extraia t e todos os valores v1.
3

Recalcule o HMAC

Calcule HMAC-SHA256 com o segredo sobre a string "{t}.{corpo}" e converta o resultado para hexadecimal.
4

Compare em tempo constante

Compare a assinatura recalculada com cada v1 recebido usando uma comparação de tempo constante (veja o aviso abaixo). Se qualquer uma conferir, a assinatura é válida.
5

Rejeite timestamps antigos (anti-replay)

Verifique que t está dentro de uma janela de tolerância (sugerimos 5 minutos) em relação ao horário atual. Isso impede que um atacante reenvie uma entrega válida capturada anteriormente.
6

Responda 2xx e deduplique

Responda com um status HTTP 2xx o mais rápido possível. As entregas são at-least-once: o mesmo evento pode chegar mais de uma vez (após retentativa ou replay). Deduplique pelo id do evento (evt_...) no corpo.
Sempre compare assinaturas em tempo constante (por exemplo, crypto.timingSafeEqual no Node.js ou hmac.compare_digest em Python). Comparações comuns de string (===, ==) param no primeiro byte diferente e vazam, pelo tempo de resposta, quantos bytes iniciais batem, abrindo um canal lateral para um atacante forjar a assinatura byte a byte. Garanta também que os dois buffers tenham o mesmo tamanho antes de comparar.

Exemplos de código

Segredo do endpoint

O segredo de assinatura pertence ao endpoint (recurso whep_...), não à organização inteira: cada endpoint tem o seu. Ele é aleatório (24 bytes), com o prefixo rwhs_, e fica disponível em claro somente nestes dois momentos:
  • na resposta de POST /webhook-endpoints (criação do endpoint);
  • na resposta de POST /webhook-endpoints/{endpointId}/rotate-secret (rotação).
Em qualquer outra resposta ou evento, o segredo aparece mascarado (rwhs_***<4 últimos>). Se você perder o valor em claro, a única forma de obter um novo é rotacionar.
Criar endpoint e capturar o segredo
Resposta (201), única vez com o secret em claro

Rotação de segredo

Rotacione o segredo periodicamente ou imediatamente se suspeitar de vazamento. A Repass mantém o segredo anterior ativo por uma janela de graça de 24 horas: durante esse período, cada entrega é assinada com os dois segredos (dois v1= no header, o novo primeiro). Depois de 24 horas, apenas o novo segredo assina. Isso permite atualizar o segredo no seu sistema sem perder nenhuma entrega.
Rotacionar o segredo
Resposta (200), novo secret em claro
O campo secret traz o novo segredo em claro (única vez). O maskedSecret é o mesmo segredo mascarado, e previousSecretExpiresAt marca o fim da janela de graça: até esse instante, as entregas são assinadas com o novo e o anterior. Fluxo recomendado de rotação sem downtime:
Aceitar qualquer v1 que confira (como nos exemplos acima) já cobre a rotação automaticamente: durante a graça, basta ter os dois segredos configurados no seu validador; assim que a graça expira, apenas o novo será enviado e validado.

Boas práticas

Trate qualquer entrega cuja assinatura não confira como não confiável: responda 400/401 e não processe o corpo. Não confie em IPs de origem nem apenas na URL secreta.
As entregas são at-least-once. Guarde os id de evento (evt_...) já processados e ignore repetições: retentativas e replays manuais são esperados.
O transporte da Repass aborta a conexão após 10 segundos. Confirme o recebimento com um 2xx imediato e empurre o trabalho pesado para uma fila; respostas lentas contam como falha e disparam retentativa.
Uma janela de 5 minutos para o t equilibra robustez contra clock skew e proteção contra replay. Ajuste conforme a precisão do relógio dos seus servidores.

Próximos passos

Webhooks: visão geral

Como funcionam as assinaturas por endpoint, o fan-out e o envelope entregue.

Retries e dead letter

Cronograma de retentativas, auto-disable por falha e replay manual.

Catálogo de eventos

Todos os tipos de evento que você pode assinar.

Event store

O histórico de eventos que alimenta os webhooks.