Verificar a assinatura HMAC do webhook: fazê-lo bem

Qualquer pessoa que conheça — ou adivinhe — o URL do seu endpoint de webhook pode fazer POST de um corpo JSON com o aspeto exato de um evento real: a mesma forma, os mesmos nomes de campo, inteiramente falso. A assinatura fecha essa lacuna: prova de que um payload veio de quem detém o segredo partilhado, e de que nada nele mudou em trânsito. A Stripe, o GitHub e a maioria dos fornecedores sérios assinam com alguma variação do mesmo esquema HMAC, por isso a mecânica aqui é quase universal — o exemplo concreto é o esquema real e atual da própria Olmira, nomes de cabeçalho incluídos.

O que o HMAC prova realmente

O HMAC (Hash-based Message Authentication Code) parte de um segredo que ambos os lados conhecem e mais ninguém conhece. O remetente aplica hash ao payload juntamente com esse segredo e anexa o resultado; do outro lado, com o mesmo segredo, recalcula e compara. Uma correspondência prova duas coisas ao mesmo tempo: a mensagem veio mesmo de alguém que detém o segredo — nenhum conhecimento do payload permite a um atacante forjar o hash — e que não foi alterada depois de assinada, porque mudar até um único caráter produz um hash completamente diferente.

O esquema exato que a Olmira usa

Cada entrega de webhook de saída transporta três cabeçalhos: X-Aether-Signature, X-Aether-Webhook-Id e X-Aether-Event. (O prefixo do cabeçalho diz Aether e não Olmira — um resto do nome de código interno da plataforma; não afeta em nada o funcionamento do esquema, apenas o nome literal do cabeçalho.)

Verificar uma entrega, em termos simples

Pegue no corpo bruto do pedido — os bytes exatos tal como entregues, antes de qualquer análise JSON — prefixe-o com o timestamp do cabeçalho de assinatura e um ponto, calcule o HMAC dessa cadeia com o segredo do seu endpoint, e compare o resultado com a assinatura entregue usando uma comparação em tempo constante. O módulo crypto incorporado do Node cobre tudo isto sem uma única dependência; as verificações prévias abaixo são onde as implementações corretas realmente se distinguem das avariadas.

Os erros que fazem falhar a verificação

Quase todos os relatos de «a minha assinatura nunca coincide» remetem para um número reduzido de causas. Fazer o hash de um corpo novamente analisado ou novamente serializado em vez dos bytes em bruto exatos é, de longe, a mais comum — os espaços, a ordem das chaves ou a formatação dos números podem mudar todos em silêncio quando um corpo passa por um analisador de JSON e é reconvertido em texto, e qualquer alteração, por mínima que seja, produz um hash diferente. Esquecer que a marca temporal faz parte do que é assinado — fazer o hash apenas do corpo, sem o prefixo . — é a segunda mais comum, e é fácil de cometer porque o corpo é a coisa mais «óbvia» a que fazer hash.

Antes de confiar nisso em produção

1

Verifique primeiro a janela de data e hora

Rejeite tudo o que estiver fora de uma tolerância pequena (a Olmira publica 300 segundos, cinco minutos, como o seu próprio valor de referência) antes de gastar tempo de CPU a calcular hashes — uma assinatura fora de prazo não merece a comparação.

2

Compare em tempo constante

Use a primitiva de comparação em tempo constante da sua linguagem, e não uma simples verificação de igualdade, para que uma divergência nunca revele, pelo tempo, quão perto esteve uma tentativa.

3

Confirme que está a aplicar hash ao corpo em bruto

Registe pelo menos uma vez, durante a configuração, a string exata que o seu código está prestes a passar por hash e compare-a, byte a byte, com o que uma entrega de teste enviou de facto.

4

Preveja a rotação da chave secreta

Onde quer que guarde o segredo, faça com que alterá-lo seja uma mudança de configuração de uma linha — rodá-lo no painel sem o atualizar no mesmo momento faz com que todas as entregas comecem a falhar até que se ponha em dia.

Como a Olmira trata disto

Cada um dos 22 eventos de webhook da Olmira — encomendas, reservas, pagamentos, faturas, pedidos de apoio e outros — é assinado exatamente da forma descrita acima, sem exceções e sem tipos de evento não assinados. Cada endpoint que regista recebe o seu próprio segredo independente, pelo que uma fuga numa integração nunca compromete outra. Antes de entrar em produção, a ação «Enviar teste» nas definições do endpoint dispara um envio de amostra real e totalmente assinado, pelo mesmo caminho de código que um evento em produção — com os mesmos cabeçalhos e a mesma forma de envelope — para que possa validar o seu verificador contra o esquema real sem esperar que uma encomenda ou reserva verdadeira o desencadeie. A plataforma de conetores completa em que isto se insere — importação CSV, recolhas agendadas e envios recebidos a par dos webhooks de saída — é Integrações.

Construa a integração uma vez e confie nela

Período experimental gratuito de 30 dias. Registe um endpoint e envie a si mesmo uma entrega de teste real e assinada em minutos.