FlowingDev

HMAC, explicado: o aperto de mão secreto digital

Aprenda como o HMAC (Hash-based Message Authentication Code) verifica a integridade e autenticidade de dados, garantindo que as mensagens não foram alteradas no caminho.

Testar a ferramenta: Gerador HMAC

Em uma frase

HMAC é um aperto de mão criptográfico que usa uma chave secreta compartilhada para provar que uma mensagem é autêntica e não foi alterada.

O problema que ele resolve

Lá nos primórdios, nos tempos de Velho Oeste da internet, enviar uma mensagem era como enviar um cartão-postal. Qualquer um que a interceptasse poderia lê-la, e talvez até rabiscar nela antes de mandá-la adiante. Se você recebesse um cartão-postal dizendo "Encontre-me à meia-noite, traga o dinheiro", como poderia ter certeza de que era mesmo do seu contato de agente secreto, e não da nêmesis dele, a Eva Maligna? E como você poderia ter certeza de que a mensagem original não era "Encontre-me ao meio-dia para um almoço amigável"?

Este é o problema duplo de autenticidade (é de você mesmo?) e integridade (foi alterada?).

Uma função de hash criptográfico simples (como SHA-256) parece um bom primeiro passo. Você poderia gerar um hash da sua mensagem, enviar a mensagem e o hash, e o destinatário poderia gerar o hash da mensagem novamente para ver se eles batem. Ótimo! Isso resolve a integridade. Se um único byte da mensagem for alterado, os hashes não vão bater.

Mas isso não resolve a autenticidade. A Eva Maligna pode simplesmente alterar a mensagem, calcular um novo hash para sua nova mensagem, e enviar ambos. O destinatário verá que o hash bate com a mensagem, mas não terá como saber que o pacote inteiro é uma falsificação.

É aqui que o HMAC (Hash-based Message Authentication Code) entra em cena. Ao introduzir uma chave secreta compartilhada no processo de hashing, o HMAC cria uma assinatura que somente alguém com essa chave secreta pode produzir. É a diferença entre um simples selo de cera (qualquer um pode fazer) e um selo de cera feito com um anel de sinete exclusivo (que só o rei tem). O HMAC nos dá tanto integridade quanto autenticidade.

Como funciona por baixo dos panos

O HMAC não é um novo tipo de função de hash; é uma receita que usa funções de hash existentes (como SHA-256) de uma maneira inteligente e específica. A especificação oficial é a RFC 2104, mas vamos simplificar em bom português.

Os Ingredientes

Para preparar um HMAC, você precisa de três coisas:

  1. A Mensagem: Os dados que você quer proteger. Isso pode ser um payload JSON para um webhook, uma string de parâmetros de URL, ou qualquer pedaço de dados binários.
  2. A Chave Secreta: Uma string de bytes conhecida apenas pelo remetente e pelo destinatário. Este é o ingrediente mágico. Se ela for comprometida, o sistema todo vai por água abaixo.
  3. A Função de Hash: Um algoritmo padrão como SHA-1, SHA-256 ou SHA-512. A escolha da função de hash determina o comprimento da assinatura HMAC final (ex: HMAC-SHA256 produz uma assinatura de 256 bits).

A Receita (O Algoritmo HMAC)

Você pode pensar que bastaria fazer hash(key + message). Parece simples, mas essa construção é vulnerável a umas artimanhas criptográficas inteligentes chamadas "ataques de extensão de comprimento" (length extension attacks). A construção oficial do HMAC é um pouco mais envolvida, especificamente para prevenir esses ataques. Ela usa um processo de hashing duplo.

Aqui está uma visão simplificada dos passos:

  1. Preparar a Chave: A função de hash opera em blocos de dados de tamanho fixo (ex: 64 bytes para SHA-256). A chave precisa ser preparada para caber nesse tamanho de bloco.

    • Se a chave for mais longa que o tamanho do bloco, você aplica o hash na própria chave e usa esse resultado como a nova chave.
    • Se a chave for mais curta que o tamanho do bloco, você a preenche com bytes zero (padding) até que ela atinja o tamanho do bloco.
  2. Criar Chaves Interna e Externa: A partir dessa chave preparada, derivamos duas chaves separadas.

    • ipad (inner pad): um byte constante (0x36) repetido para preencher o tamanho do bloco.
    • opad (outer pad): um byte constante diferente (0x5C) repetido para preencher o tamanho do bloco.

    Nós criamos uma inner_padded_key (chave com padding interno) pegando nossa chave preparada e fazendo um XOR com o ipad. Nós criamos uma outer_padded_key (chave com padding externo) fazendo um XOR da chave preparada com o opad.

  3. Executar o Hash Duplo: Agora, o evento principal.

    • Hash Interno: Concatene a inner_padded_key com a mensagem original, e passe o resultado pela função de hash.
    • Hash Externo: Concatene a outer_padded_key com o resultado do hash interno, e passe isso pela função de hash.

O resultado final desse hash externo é a sua assinatura HMAC!

Em pseudocódigo, fica assim:

function hmac(key, message, hash_function, block_size) {

  // 1. Prepare the key
  if (key.length > block_size) {
    key = hash_function(key);
  }
  if (key.length < block_size) {
    key = pad_with_zeros(key, block_size);
  }

  // 2. Create inner and outer padded keys
  o_key_pad = key XOR (0x5C repeated to block_size);
  i_key_pad = key XOR (0x36 repeated to block_size);

  // 3. Perform the double hash
  inner_hash_result = hash_function(i_key_pad + message);
  final_hmac = hash_function(o_key_pad + inner_hash_result);

  return final_hmac;
}

Por que o Hash Duplo?

Essa estrutura de hash interno e depois externo é o pulo do gato. O hash interno combina o segredo e a mensagem. O hash externo, então, essencialmente aplica o hash novamente no resultado da primeira operação, junto com o segredo. Isso "sela" o hash interno. Isso torna computacionalmente impossível para um invasor manipular o resultado do hash intermediário sem conhecer a chave, frustrando assim os "length extension attacks" e outras possíveis quebras criptográficas. É uma construção comprovada e robusta que resistiu ao teste do tempo.

Histórias do mundo real

O Guardião do Webhook do GitHub

A equipe de uma startup configurou seu servidor de integração contínua (CI) para fazer o deploy automático do app para produção toda vez que alguém desse um push na branch main. O gatilho era um webhook do GitHub: uma requisição POST enviada dos servidores do GitHub para uma URL pública no servidor de CI deles. Certa noite, o estagiário brincalhão da equipe encontrou a URL pública e, usando um simples comando cURL, começou a enviar payloads de webhooks falsos, disparando dezenas de deploys inúteis que consumiam um monte de recursos.

A desenvolvedora sênior resolveu o problema em 15 minutos. Nas configurações de webhook do GitHub, ela gerou um "segredo" longo e aleatório. Ela copiou esse segredo e o configurou como uma variável de ambiente no servidor de CI. O GitHub passou a usar esse segredo para gerar uma assinatura HMAC-SHA256 para cada payload de webhook, enviando-a junto em um header X-Hub-Signature-256. O código do servidor de CI foi atualizado para realizar o mesmo cálculo HMAC no corpo da requisição (raw body) que recebia, usando o mesmo segredo. Se a assinatura calculada batesse com a do header, a requisição era processada. Caso contrário, era rejeitada com um 403 Forbidden. As brincadeiras pararam na hora.

Lição: Sempre proteja seus webhooks com verificação de assinatura HMAC. Não confie em nenhuma requisição de entrada até que ela seja autenticada.

Protegendo o Pote de Cookies da API

Um desenvolvedor estava construindo um serviço que usava um cookie simples e assinado para autenticação. Quando um usuário fazia login, o servidor emitia um cookie contendo seu user_id e um timestamp de expiry (expiração). Para impedir que os usuários editassem seu cookie para se tornarem outro usuário (ex: alterando user_id=123 para user_id=1), o desenvolvedor incluiu uma assinatura HMAC.

O payload do cookie era algo assim: user_id=123&expiry=1678886400. O servidor assinava essa string exata com uma chave secreta armazenada no servidor. O cookie final enviado para o navegador era data="user_id=123&expiry=1678886400"&signature="sha1=2a8b...".

Quando o usuário fazia uma requisição seguinte, seu navegador enviava o cookie de volta. O servidor pegava a parte data, recalculava a assinatura HMAC usando sua chave secreta e a comparava com a parte signature do cookie. Se batessem, o servidor sabia que o ID do usuário e a data de expiração eram legítimos e não haviam sido adulterados.

Lição: HMAC é uma forma fantástica de criar tokens ou cookies "stateless" (sem estado) e à prova de adulteração, formando a base de muitos sistemas de autenticação, incluindo os JSON Web Tokens (JWTs).

A Transferência Bancária que não foi Sequestrada

Uma plataforma de e-commerce integrou-se com a API de um processador de pagamentos para iniciar pagamentos aos seus vendedores. A chamada da API era uma mensagem JSON simples: {"vendor_id": "ven_abc", "amount": 500.00, "currency": "USD"}. A plataforma estava preocupada com um ataque man-in-the-middle (MITM). Mesmo sobre HTTPS, que criptografa o tráfego, um invasor sofisticado poderia (em alguns cenários teóricos, como com uma autoridade certificadora comprometida) interceptar e modificar a requisição. Eles poderiam alterar o amount para 50000.00 ou o vendor_id para o deles.

A API do processador de pagamentos exigia que toda requisição fosse assinada com HMAC-SHA512. A plataforma serializava o payload JSON em uma string canônica, calculava a assinatura com sua chave de API privada e a enviava em um header Authorization. Os servidores do processador de pagamento realizariam exatamente os mesmos passos. Se a assinatura calculada batesse com a enviada no header, eles sabiam duas coisas com certeza: que a requisição veio da plataforma legítima (autenticidade) e que o vendor_id e o amount não foram alterados no caminho (integridade).

Lição: Para operações de alto risco, o HMAC fornece uma camada crítica de segurança para garantir que o remetente e o conteúdo de uma mensagem são o que você espera.

Erros e armadilhas comuns

  • Vazar a chave secreta. A chave é tudo. Se você a expuser no JavaScript do lado do cliente, comitá-la em um repositório Git público ou registrá-la em logs como texto puro, sua segurança já era. Trate-a como uma senha.
  • Usar uma comparação que não seja de tempo constante. Ao verificar se a assinatura fornecida pelo usuário bate com a que você calculou, uma comparação de strings padrão como if (a === b) pode ser uma falha de segurança. Ela geralmente retorna false assim que encontra um caractere diferente. Isso cria uma minúscula diferença de tempo que invasores podem medir para adivinhar a assinatura, um caractere por vez. Isso é um "ataque de temporização" (timing attack). Sempre use uma função de comparação dedicada, de "tempo constante", de uma biblioteca de criptografia, que leva a mesma quantidade de tempo independentemente de onde a diferença ocorra.
  • Assinar os dados errados. O remetente e o destinatário devem calcular o HMAC sobre a mesma sequência exata de bytes. Um bug comum é um lado assinar um objeto JSON "embelezado" (prettified) enquanto o outro assina a versão compacta, de uma linha só. Ou um lado inclui uma quebra de linha no final e o outro não. Vocês precisam concordar em um formato de mensagem canônico e se ater a ele.
  • Esquecer dos "replay attacks" (ataques de repetição). O HMAC por si só não impede um invasor de capturar uma mensagem válida e assinada e simplesmente reenviá-la várias e várias vezes. Se essa mensagem for "pague $10 ao Bob", você não quer que o invasor possa disparar esse pagamento 1000 vezes. Para evitar isso, inclua um valor que muda a cada requisição — como um timestamp ou um "nonce" (número usado uma vez) — dentro dos dados que estão sendo assinados. O servidor pode então verificar o timestamp para rejeitar requisições antigas ou manter uma lista de nonces já usados para rejeitar duplicatas.

Por que isso deve estar no seu radar

Você deve pensar em HMAC sempre que estiver lidando com comunicação que precisa ser confiável. Não se trata de manter os dados secretos (isso é trabalho da criptografia), mas de garantir que os dados são legítimos.

  • Construindo ou consumindo APIs? Especialmente com webhooks (de serviços como Stripe, GitHub, Twilio), o HMAC é o padrão da indústria para verificar se a requisição é autêntica.
  • Trabalhando com autenticação? Muitos sistemas baseados em tokens, sendo os JWTs os mais famosos, usam HMAC (ex: o algoritmo 'HS256') para assinar o payload do token, impedindo que os usuários modifiquem suas próprias permissões.
  • Precisa verificar a integridade dos dados? Se você está passando dados por um ambiente não confiável (como o navegador do usuário em um cookie) e precisa garantir que eles voltem sem modificações, HMAC é a sua ferramenta.

É uma primitiva fundamental na caixa de ferramentas de segurança de um desenvolvedor web. Entender como ele funciona fará de você um engenheiro melhor e mais consciente sobre segurança.

Aprofunde-se

Teoria feita. Hora de pôr a mão na massa — 100% no seu navegador.

Testar a ferramenta: Gerador HMAC