Gerador de HMAC

Calcula HMAC-SHA-1, SHA-256, SHA-384 e SHA-512 a partir de uma mensagem e de uma chave, e identifica de qual veio uma assinatura dada.

Mensagem
HMAC-SHA-1
HMAC-SHA-256
HMAC-SHA-384
HMAC-SHA-512

Introduza uma chave para calcular as assinaturas.

O que esta ferramenta faz

O HMAC transforma uma mensagem e um segredo compartilhado numa assinatura curta. Quem tiver o mesmo segredo pode recalculá-la e ver se a mensagem chegou sem alterações e se veio de alguém que também conhece o segredo. Esta ferramenta calcula as quatro variantes habituais de uma vez e, dada uma assinatura que lhe enviaram, diz a você qual delas a produziu.

Esse segundo sentido costuma ser a razão por que as pessoas chegam aqui. Um webhook falha a verificação, e a pergunta verdadeira não é «esta assinatura é válida» mas «qual das várias coisas plausíveis estou fazendo de forma diferente do remetente». Um prefixo do tipo sha256= à frente de uma assinatura colada é reconhecido e removido, portanto o valor do cabeçalho pode ser colado exatamente como chegou.

A chave é onde isto dá errado

O HMAC assina bytes com bytes. A mensagem costuma ser evidente — o corpo do pedido em bruto — mas a chave quase nunca é, porque um segredo chega como cadeia e uma cadeia só é bytes depois de decidir como a ler:

a3f2  ->  61 33 66 32   4 bytes   como texto
a3f2  ->  a3 f2         2 bytes   como hex

As duas leituras são legítimas, ambas produzem uma assinatura perfeitamente bem formada, e as duas assinaturas não têm nada em comum. Nada o avisa: não há erro, não há queixa de comprimento, apenas um valor que não coincide com o que o remetente calculou. É por isso que aqui a codificação da chave é uma escolha visível e não um palpite: quando uma assinatura não coincide, é a primeira coisa a trocar.

Como orientação grosseira: um segredo com um prefixo como whsec_ ou uma sequência de letras maiúsculas e minúsculas e dígitos costuma ser lido como texto; uma cadeia de exatamente 32 ou 64 caracteres que use apenas 0-9 e a-f é normalmente hexadecimal; e uma que termine em = é quase de certeza Base64. Mas consulte a documentação do remetente em vez da forma, porque a forma não é prova.

A mensagem tem de ser os bytes exatos

A outra metade de uma divergência é a mensagem. O HMAC é definido sobre bytes, por isso tudo o que mude os bytes muda a assinatura por completo: não há classificação parcial nem quase acerto.

  • Voltar a serializar o JSON. Analisar um corpo e voltar a gerá-lo pode reordenar chaves, mudar espaços ou normalizar números. Assine e verifique o corpo em bruto que recebeu, nunca uma cópia que foi e voltou.
  • Uma quebra de linha final. Algumas ferramentas acrescentam uma ao salvar o corpo em um arquivo; é um byte, e muda tudo.
  • A codificação de caracteres. Um corpo com texto não ASCII tem de ser lido com a mesma codificação dos dois lados — na prática, UTF-8.
  • Compressão ou middleware. Se algo à frente do seu manipulador descomprime ou reescreve o corpo, verifique antes de isso acontecer, não depois.

Muitos fornecedores também não assinam apenas o corpo. A Stripe assina uma marca temporal e o corpo unidos por um ponto; a AWS assina um pedido canónico construído a partir do método, do caminho, dos cabeçalhos e de um hash da carga. Se a verificação falhar contra o corpo simples, a cadeia assinada provavelmente não é o corpo simples — isso está documentado do lado do remetente e vale a pena ler antes de continuar a depurar.

Por que o SHA-1 é oferecido aqui e não na ferramenta de hash

A ferramenta de hash marca o SHA-1 como quebrado. Esta lista o HMAC-SHA-1 sem aviso, e isso é deliberado, não um descuido.

O SHA-1 é inútil para assinaturas e certificados porque se conseguem construir colisões: dois documentos diferentes podem ser levados a compartilhar o mesmo resumo. O HMAC não depende dessa propriedade. A sua segurança assenta na chave secreta, e a construção — resumir a mensagem duas vezes, com a chave misturada em ambas — aguenta mesmo quando o hash subjacente é fraco a colisões. O HMAC-SHA-1 continua sólido e é o que o OAuth 1.0a e a assinatura antiga da AWS usam, por isso uma ferramenta que se recusasse a calculá-lo seria simplesmente menos útil sem ser mais segura.

Para algo novo, o SHA-256 é a escolha sensata. Resumos mais longos não são significativamente mais fortes aqui — o teto de segurança é a chave, não o comprimento do resumo — por isso o SHA-384 e o SHA-512 só valem a pena quando algo com que tem de interoperar os pede.

Comparar assinaturas em segurança

Esta página compara com uma comparação de cadeias normal, e aqui isso está bem: a chave está com você, por isso não há nada que possa vazar. Num servidor que verifica um pedido que chega, não está bem. Uma comparação normal para no primeiro byte diferente, por isso o tempo que demora revela que parte de um palpite estava correta, e um atacante que possa enviar muitos pedidos consegue recuperar uma assinatura válida byte a byte.

Use a comparação em tempo constante que a sua plataforma oferece — crypto.timingSafeEqual em Node, hmac.compare_digest em Python, hash_equals em PHP — sobre os bytes em bruto e não sobre cadeias hexadecimais. A par disso, compare com uma assinatura que tenha calculado e não confie num algoritmo indicado no pedido, e recuse uma mensagem cuja marca temporal esteja longe de agora, para que uma assinatura válida antiga não possa ser repetida.

O HMAC não é um hash nem uma assinatura

Aqui confundem-se três coisas, e a diferença importa para o que se pode afirmar depois:

  • Um hash recebe uma mensagem e produz um resumo. Qualquer pessoa o pode calcular, por isso prova que a mensagem não mudou por acidente, não que veio de alguém em particular.
  • Um HMAC recebe uma mensagem e um segredo compartilhado. As duas partes têm a mesma chave, por isso prova que o remetente conhecia o segredo. Não pode provar qual das duas enviou, porque qualquer uma o poderia ter produzido.
  • Uma assinatura usa uma chave privada que só o remetente tem, e uma chave pública com que qualquer um pode verificar. É isso que dá o não repúdio: o remetente não a pode negar depois.

Assim, o HMAC é a ferramenta certa entre dois sistemas que já compartilham um segredo — webhooks, APIs internas, tokens de sessão — e a errada quando é preciso provar a um terceiro quem enviou alguma coisa. Note também que autentica mas não esconde: a mensagem viaja em claro, e o HMAC nada diz sobre confidencialidade.

Verificar a assinatura de um webhook da Stripe

A Stripe é a única receita aqui que não é uma colagem, e normalmente é a que as pessoas já tentaram antes de chegar. O cabeçalho Stripe-Signature é uma lista de elementos separados por vírgulas e não uma assinatura, e a cadeia que a Stripe assinou não é o corpo do pedido sozinho. A própria documentação da Stripe quebra o cabeçalho em linhas para facilitar a leitura; um cabeçalho real chega em uma única linha.

Stripe-Signature:
t=1492774577,
v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,
v0=6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39
  • Mensagem: o valor de t=, depois um ponto, depois o corpo do pedido em bruto exatamente como chegou. Para o cabeçalho acima, essa cadeia começa com 1492774577. e o corpo vem logo em seguida. A Stripe chama isso a carga assinada, e é toda a razão por que uma assinatura da Stripe nunca coincide com o corpo sozinho.
  • Chave: o segredo de assinatura daquele endpoint específico, inteiro, incluindo o prefixo whsec_. Não é a sua chave de API, nem o segredo de outro endpoint — a mesma URL registrada duas vezes tem dois.
  • Codificação da chave: Texto. Um segredo de assinatura é uma cadeia que a Stripe escolheu e, mesmo parecendo aleatório, não é hexadecimal nem Base64; ler como um dos dois dá bytes diferentes e uma assinatura que não coincide com nada.
  • Assinatura a verificar: apenas o elemento v1, não o cabeçalho inteiro. O v1= pode ser colado como está, porque essa etiqueta é removida exatamente como sha256= — mas o cabeçalho completo não, porque o t= inicial é lido como a etiqueta e o que vem depois dele não é assinatura nenhuma.

Vale saber outras três coisas sobre este cabeçalho antes de investigar qualquer outra. Ignore todo esquema que não seja v1: o elemento v0 nos eventos de teste não é uma assinatura verdadeira de propósito, e ignorar o resto é justamente o que impede que um esquema mais fraco seja imposto a você. Enquanto o segredo de um endpoint está sendo rotacionado, os dois continuam válidos por até um dia e o cabeçalho carrega um elemento v1 por segredo, dos quais só um vai coincidir. E cada tentativa de entrega é assinada de novo, então uma nova tentativa não carrega a assinatura da primeira — a marca temporal dentro da cadeia assinada também é o que torna a recusa de uma assinatura antiga uma defesa real em vez de um gesto, porque não dá para alterá-la sem quebrar a assinatura.

Verificar a assinatura de um webhook do GitHub

O GitHub é a colagem em torno da qual esta ferramenta foi construída, e a única receita aqui que você pode conferir de ponta a ponta, porque o GitHub publica um exemplo resolvido. A assinatura chega em X-Hub-Signature-256 como a etiqueta sha256= seguida de um resumo hexadecimal, e essa etiqueta é reconhecida e removida aqui, então o valor do cabeçalho entra exatamente como chegou.

X-Hub-Signature-256:
sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17
  • Mensagem: o corpo do pedido em bruto, byte a byte. O exemplo publicado pelo GitHub assina o corpo Hello, World! e nada depois dele — sem quebra de linha final.
  • Chave: o segredo que você digitou nas configurações do webhook, exatamente como o digitou. O exemplo publicado usa It's a Secret to Everybody.
  • Codificação da chave: Texto. Um segredo do GitHub é uma cadeia que você escolheu, portanto nunca é hexadecimal — até um segredo formado só por dígitos hexadecimais é lido como os caracteres que você digitou.
  • Assinatura a verificar: o valor inteiro do cabeçalho, sha256= incluído. O hexadecimal é comparado sem distinguir maiúsculas de minúsculas, então não importa de que forma os seus registros o imprimem.

Esses três valores juntos são a checagem de sanidade mais rápida que existe: cole-os e esta página informa uma coincidência com HMAC-SHA-256 em hexadecimal, o que diz a você que tanto a calculadora quanto a sua leitura da receita estão corretas antes de testar qualquer uma delas numa entrega real. O GitHub envia também um cabeçalho mais antigo, X-Hub-Signature, que é HMAC-SHA-1 sobre o mesmo corpo e é mantido apenas por compatibilidade com o passado; como esta página calcula os quatro resumos de uma vez, uma assinatura de qualquer um dos dois cabeçalhos é identificada sem que você tenha de dizer de qual. A única coisa que ela não consegue salvar é um corpo que chegou como algo diferente dos bytes que o GitHub enviou — as cargas podem trazer caracteres fora do ASCII, e os dois lados precisam lê-los como UTF-8.

Verificar a assinatura de um webhook da Shopify

A Shopify escreve o seu resumo em Base64 e não em hexadecimal, e essa é a única diferença que importa aqui: os mesmos 32 bytes viram 44 caracteres terminando num único sinal de igual, e esse caractere é enchimento e não uma etiqueta, portanto nada é cortado da frente dele.

X-Shopify-Hmac-SHA256:
dXEH6g6yUJ/CESIczphLijdXC211hsIsRvQ3nIsEPhc=
  • Mensagem: o corpo do pedido em bruto, byte a byte como foi entregue. O próprio aviso da Shopify é sobre o middleware que analisa o corpo — verifique primeiro e analise depois, porque um analisador que já transformou o corpo em objeto jogou os bytes fora.
  • Chave: o segredo de cliente do aplicativo a que o webhook pertence. Não o token de acesso dele, nem a chave de API ao lado no mesmo painel.
  • Codificação da chave: Texto. Como nos outros dois, o segredo é uma cadeia e não uma codificação de bytes.
  • Assinatura a verificar: o valor inteiro do cabeçalho. Ele pode ser colado como está e, como hexadecimal e Base64 são ambos testados contra cada resumo, a codificação que esta página informa é ela mesma a resposta a qual grafia o remetente usou.

O valor no bloco acima está ali para mostrar a forma: são os mesmos 32 bytes do exemplo do GitHub, escritos em Base64 em vez de hexadecimal. Vale a pena vê-los lado a lado, porque é todo o conteúdo da diferença entre os dois cabeçalhos — um resumo, duas grafias — e é por isso que esta página nomeia a codificação junto ao algoritmo em vez de apenas dizer a você que algo coincidiu.

Perguntas frequentes

A minha assinatura não coincide. O que verifico primeiro?
A codificação da chave e depois os bytes da mensagem. Um segredo que parece hexadecimal é muitas vezes pretendido como hexadecimal e não como texto, e os dois produzem assinaturas completamente diferentes sem erro de qualquer dos lados. Depois, certifique-se de que assina o corpo em bruto tal como foi recebido e não uma cópia voltada a serializar.
Por que o SHA-1 é oferecido aqui se a ferramenta de hash diz que está quebrado?
Porque o HMAC não assenta na resistência a colisões, que é a propriedade que o SHA-1 perdeu. A sua segurança vem da chave. O HMAC-SHA-1 continua sólido e em uso pelo OAuth 1.0a e pela assinatura antiga da AWS, embora o SHA-256 seja o padrão certo para algo novo.
Um resumo mais longo é mais seguro?
Não de forma significativa. A força de um HMAC é limitada pelo segredo e não pelo comprimento do resumo, por isso o SHA-512 não é quatro vezes melhor do que o SHA-256. Escolha o que a outra parte espera.
Qual é a diferença entre HMAC e uma assinatura digital?
O HMAC usa um segredo que ambas as partes conhecem, por isso prova que o remetente conhecia o segredo mas não qual das partes enviou. Uma assinatura digital usa uma chave privada que só o remetente tem, de modo que um terceiro a pode verificar e o remetente não a pode negar. Se precisa dessa última propriedade, o HMAC é a ferramenta errada.
Devo comparar assinaturas com ===?
Num servidor, não. Uma comparação normal regressa assim que os bytes diferem, e esse tempo revela que parte de uma assinatura adivinhada estava certa. Use a comparação em tempo constante da sua plataforma sobre os bytes em bruto. Nesta página não importa, porque a chave já está consigo.
A minha chave é enviada para algum lugar?
Não. Tudo é calculado no seu navegador com a Web Crypto API; a mensagem e a chave nunca saem do seu dispositivo.
Como verifico a assinatura de um webhook da Stripe?
Junte três coisas numa única cadeia — a marca temporal do elemento t= do cabeçalho, um ponto e o corpo do pedido em bruto — e assine essa cadeia com o segredo de assinatura do endpoint lido como texto; depois compare o resultado com o elemento v1 do cabeçalho. A seção acima percorre isso campo por campo. O que derruba quase todo mundo é o primeiro passo: o corpo sozinho não é o que a Stripe assinou.
Como verifico a assinatura de um webhook do GitHub?
Assine o corpo do pedido em bruto com o segredo do webhook lido como texto, em SHA-256, e compare o resumo hexadecimal com o cabeçalho X-Hub-Signature-256. A etiqueta sha256= pode ficar quando você a cola aqui. O GitHub publica um exemplo resolvido — o corpo Hello, World! com o segredo It's a Secret to Everybody — e é o jeito mais rápido de conferir a sua leitura da receita antes de testá-la numa entrega real.
Como verifico a assinatura de um webhook da Shopify?
Assine o corpo do pedido em bruto com o segredo de cliente do aplicativo a que o webhook pertence, lido como texto, em SHA-256, e compare o resumo em Base64 com o cabeçalho X-Shopify-Hmac-SHA256. O valor inteiro do cabeçalho pode ser colado como está — o sinal de igual no fim é enchimento do Base64. Quando não coincide, a causa habitual é um middleware que analisou o corpo antes de o seu manipulador chegar a vê-lo.
Como verifico a assinatura de um webhook de qualquer outro provedor?
Quatro perguntas resolvem, e a documentação do remetente tem as quatro: qual cabeçalho carrega a assinatura, qual cadeia é realmente assinada, como o segredo deve ser lido, e se o resumo é hexadecimal ou Base64. Preencha isso nos campos acima. Se ainda não coincidir, a resposta é quase sempre a segunda — muitíssimos remetentes assinam o corpo com algo unido a ele em vez do corpo sozinho.

Ferramentas relacionadas