Capa do Ebook gratuito Criptografia Aplicada para Profissionais: o que usar, quando e por quê

Criptografia Aplicada para Profissionais: o que usar, quando e por quê

Novo curso

22 páginas

Autenticidade de mensagens com HMAC e comparação com assinaturas

Capítulo 6

Tempo estimado de leitura: 13 minutos

+ Exercício

O que é autenticidade de mensagens e onde o HMAC entra

Autenticidade de mensagens é a garantia de que um conteúdo recebido foi produzido por alguém que possui uma determinada credencial criptográfica e que o conteúdo não foi alterado no caminho. Na prática, isso significa duas propriedades: (1) integridade (qualquer modificação no conteúdo é detectada) e (2) autenticação do emissor (somente quem possui o segredo correto consegue gerar um “selo” válido para aquela mensagem).

HMAC (Hash-based Message Authentication Code) é um mecanismo de autenticação de mensagens baseado em uma função hash e em uma chave secreta compartilhada. Ele produz um “tag” (ou MAC) que acompanha a mensagem. Quem recebe, usando a mesma chave secreta, recalcula o HMAC e compara com o tag recebido. Se bater, a mensagem é considerada autêntica e íntegra.

É importante separar HMAC de “hash puro”. Um hash sem chave (por exemplo, SHA-256) não autentica nada: qualquer pessoa pode calcular o hash de uma mensagem. O HMAC adiciona a chave secreta, tornando o tag impossível de ser forjado por quem não conhece a chave.

Como o HMAC funciona (visão prática sem entrar em história)

O HMAC combina uma função hash (como SHA-256) com uma chave secreta K e a mensagem M. O resultado é um tag T. A construção HMAC é padronizada e desenhada para evitar armadilhas comuns de “hash com chave” improvisado (por exemplo, concatenar chave e mensagem de forma ingênua).

Você normalmente verá HMAC como HMAC-SHA-256, HMAC-SHA-384, etc. O tamanho do tag depende do hash (por exemplo, 256 bits para SHA-256), mas muitas APIs permitem truncar o tag para um tamanho menor. Truncar pode ser aceitável em alguns cenários, mas deve ser uma decisão consciente: quanto menor o tag, maior a chance de colisão por tentativa e erro (força bruta) e menor a margem de segurança.

Continue em nosso aplicativo

Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.

ou continue lendo abaixo...
Download App

Baixar o aplicativo

Propriedades relevantes para uso profissional

  • Chave compartilhada: emissor e receptor precisam ter a mesma chave secreta. Isso simplifica e acelera, mas complica distribuição/rotação de chaves quando há muitos participantes.
  • Desempenho: HMAC é rápido e eficiente, adequado para alto volume (APIs, microserviços, filas, eventos).
  • Não fornece não-repúdio: como a chave é compartilhada, qualquer parte que possua a chave pode ter gerado o tag. Isso muda o tipo de evidência que você consegue produzir em auditorias e disputas.
  • Composição com criptografia: HMAC autentica; ele não cifra. Se você precisa confidencialidade, combine com criptografia apropriada (ou use um esquema que já forneça confidencialidade e autenticidade). Evite “inventar” combinações sem padrão.

Quando usar HMAC (e quando não usar)

Casos típicos em que HMAC é uma boa escolha

  • Autenticação de chamadas entre serviços: um serviço A chama B e inclui um HMAC sobre partes da requisição. B valida e aceita apenas se o tag for válido.
  • Proteção de webhooks: o provedor envia um evento e assina com HMAC; o consumidor valida para garantir que o evento veio do provedor e não foi alterado.
  • Integridade de mensagens em filas: produtores e consumidores compartilham uma chave para autenticar payloads.
  • Tokens simples de integridade: por exemplo, um identificador e um timestamp assinados com HMAC para evitar adulteração no cliente.

Casos em que HMAC costuma ser inadequado

  • Ambientes com muitos emissores independentes: se cada cliente precisa assinar mensagens e você não quer que clientes compartilhem uma mesma chave, HMAC vira um problema operacional (muitas chaves, risco de vazamento, dificuldade de atribuição).
  • Necessidade de não-repúdio: se você precisa provar para terceiros que “foi o usuário X que assinou”, HMAC não resolve, porque qualquer detentor da chave poderia ter assinado.
  • Distribuição de confiança assimétrica: quando o verificador não deve ter capacidade de forjar mensagens. Com HMAC, quem verifica também consegue gerar tags válidos.

Comparação direta: HMAC vs assinaturas digitais

HMAC e assinaturas digitais podem parecer equivalentes (“um selo criptográfico sobre a mensagem”), mas atendem necessidades diferentes.

Modelo de chaves e implicações

  • HMAC: chave simétrica compartilhada. Todos que verificam precisam da mesma chave que assina. Consequência: qualquer verificador pode forjar mensagens.
  • Assinatura digital: chave privada assina, chave pública verifica. Verificadores não conseguem assinar. Consequência: separação de papéis e melhor auditabilidade.

Escalabilidade e gestão de chaves

  • HMAC: para N pares independentes, você pode acabar com muitas chaves (por exemplo, uma por cliente). Rotação exige coordenação bilateral. Em sistemas com múltiplos serviços internos, pode ser simples se você já tem um cofre de segredos e automação de rotação.
  • Assinaturas: um emissor pode publicar sua chave pública e qualquer um verifica. Rotação pode ser feita publicando nova chave pública e mantendo um período de transição com múltiplas chaves válidas.

Desempenho

  • HMAC: muito rápido, baixo custo computacional, ideal para alta taxa de requisições.
  • Assinaturas: mais custosas (especialmente em hardware limitado ou alto volume). Ainda assim, podem ser viáveis dependendo do algoritmo e do throughput.

Não-repúdio e auditoria

  • HMAC: não oferece não-repúdio. Se houver disputa, não dá para provar qual parte com a chave gerou o MAC.
  • Assinaturas: permitem atribuição ao detentor da chave privada (assumindo controle adequado da chave e trilhas de auditoria). Em cenários regulatórios, isso costuma ser decisivo.

Exemplos de decisão

Webhook de provedor para seu sistema: HMAC é comum e adequado, porque há um relacionamento bilateral (você e o provedor) e o objetivo é garantir integridade/autenticidade do evento.

Documento assinado por um usuário final para ser verificado por múltiplas partes: assinatura digital é mais apropriada, porque qualquer verificador pode validar sem ter poder de forjar.

Comunicação interna entre microserviços no mesmo domínio de confiança: HMAC pode ser suficiente e mais eficiente, desde que a gestão de segredos seja madura.

Projeto do que assinar: canonicalização e seleção de campos

Uma das maiores fontes de falhas em autenticação de mensagens não é o algoritmo, mas o que exatamente foi assinado. Se emissor e receptor não concordam sobre a representação exata, validações falham ou, pior, abrem brechas.

Canonicalização (normalização) do conteúdo

Se você assina JSON “como string”, diferenças de espaços, ordem de campos ou formatação podem quebrar a validação. Estratégias comuns:

  • Assinar bytes exatos do corpo: o emissor envia o corpo e o receptor valida exatamente aqueles bytes. Isso funciona bem para webhooks e APIs quando você tem acesso ao corpo bruto.
  • Canonical JSON: definir uma forma canônica (ordem de chaves, sem espaços, encoding fixo). Isso exige biblioteca e padrão consistente.
  • Assinar um conjunto de campos: em vez do corpo inteiro, assinar campos críticos concatenados com delimitadores bem definidos e regras de escaping.

Quais campos incluir

Em requisições HTTP, é comum incluir no material assinado:

  • Método (GET/POST), caminho e query string (ou uma versão canônica dela)
  • Timestamp
  • Nonce (opcional, para evitar replay em janelas curtas)
  • Hash do corpo (ou o corpo bruto)
  • Identificador da chave (key id) para rotação

O objetivo é evitar que um atacante reaproveite um tag válido em outro contexto (por exemplo, mudar o endpoint, trocar parâmetros, ou repetir a mesma requisição mais tarde).

Passo a passo prático: autenticação de requisições HTTP com HMAC

A seguir, um fluxo prático e comum para autenticar requisições entre cliente e servidor usando HMAC. A ideia é simples: o cliente cria uma “string para assinatura” determinística, calcula o HMAC e envia o resultado em um header. O servidor reconstrói a mesma string e valida.

1) Defina o formato da string para assinatura

Exemplo de string para assinatura (cada linha é um componente):

METHOD
PATH
QUERY
TIMESTAMP
BODY_SHA256

Regras:

  • METHOD: em maiúsculas (GET, POST).
  • PATH: exatamente como recebido (por exemplo, /v1/payments).
  • QUERY: query string canônica (por exemplo, parâmetros ordenados por nome e URL-encoded).
  • TIMESTAMP: Unix epoch em segundos (ou ISO-8601, mas seja consistente).
  • BODY_SHA256: hash do corpo em bytes (se não houver corpo, hash de string vazia).

2) Cliente: calcule o HMAC e envie headers

Headers típicos:

  • X-Auth-KeyId: identificador da chave (para rotação)
  • X-Auth-Timestamp: timestamp usado
  • X-Auth-Signature: HMAC em Base64 (ou hex)

Pseudocódigo do cliente:

key = lookup_secret(key_id)  # bytes, vindo de um cofre de segredos, não hardcoded
body_hash = SHA256(body_bytes)
string_to_sign = METHOD + "\n" + PATH + "\n" + CANONICAL_QUERY + "\n" + TIMESTAMP + "\n" + HEX(body_hash)
tag = HMAC_SHA256(key, UTF8(string_to_sign))
signature = BASE64(tag)
send headers: X-Auth-KeyId, X-Auth-Timestamp, X-Auth-Signature

3) Servidor: valide timestamp e reconstrua a string

Validações recomendadas antes de comparar HMAC:

  • Janela de tempo: rejeite timestamps muito antigos/futuros (por exemplo, mais de 5 minutos). Isso reduz replay.
  • KeyId: busque a chave correta pelo identificador; se não existir, rejeite.
  • Canonicalização: reconstrua METHOD/PATH/QUERY exatamente com as mesmas regras do cliente.

Pseudocódigo do servidor:

key = lookup_secret(key_id)
if abs(now() - timestamp) > 300: reject
body_hash = SHA256(body_bytes)
string_to_sign = METHOD + "\n" + PATH + "\n" + CANONICAL_QUERY + "\n" + TIMESTAMP + "\n" + HEX(body_hash)
expected_tag = HMAC_SHA256(key, UTF8(string_to_sign))
expected_signature = BASE64(expected_tag)
if constant_time_equals(expected_signature, received_signature): accept else reject

4) Comparação em tempo constante

Ao comparar o tag recebido com o calculado, use comparação em tempo constante (constant-time). Comparações ingênuas podem vazar informação por tempo de execução (timing side-channel), permitindo que um atacante adivinhe o tag byte a byte em alguns cenários.

Em muitas linguagens e bibliotecas, existe uma função pronta para isso (por exemplo, “constant time compare”). Se não houver, evite implementar manualmente sem cuidado.

5) Proteção contra replay (além de timestamp)

Timestamp com janela curta reduz replay, mas não elimina. Para endpoints sensíveis (por exemplo, criação de pagamento), considere:

  • Nonce: um valor aleatório por requisição incluído na string assinada.
  • Armazenamento de nonces: o servidor guarda nonces vistos dentro da janela e rejeita repetidos.
  • Idempotency key: um identificador de operação que o servidor usa para garantir que a mesma operação não seja aplicada duas vezes, mesmo que a requisição seja repetida.

Passo a passo prático: validação de webhooks com HMAC

Webhooks são um caso clássico: um provedor envia eventos para sua URL. Você precisa garantir que o evento veio do provedor e que o corpo não foi alterado.

1) Assine o corpo bruto

O padrão mais robusto é assinar os bytes exatos do corpo HTTP (raw body). Isso evita problemas de canonicalização de JSON.

2) Inclua timestamp no header

Um formato comum é enviar algo como:

  • X-Webhook-Timestamp: epoch
  • X-Webhook-Signature: Base64(HMAC(secret, timestamp + "." + raw_body))

Pseudocódigo do provedor:

payload = raw_body_bytes
msg = ASCII(timestamp) + b"." + payload
sig = BASE64(HMAC_SHA256(secret, msg))

3) Consumidor valida

if abs(now() - timestamp) > 300: reject
msg = ASCII(timestamp) + b"." + raw_body_bytes
expected = BASE64(HMAC_SHA256(secret, msg))
if constant_time_equals(expected, received): accept else reject

Se você fizer parse do JSON e reserializar antes de validar, pode quebrar a assinatura. A validação deve ocorrer sobre o corpo bruto.

Erros comuns ao usar HMAC em sistemas reais

1) Assinar “o que é conveniente” em vez do que é necessário

Exemplo: assinar apenas o corpo e esquecer método/path/query. Um atacante pode reaproveitar um corpo válido em outro endpoint ou mudar parâmetros críticos na URL. Se o contexto muda o significado, o contexto deve estar assinado.

2) Não validar janela de tempo

Sem timestamp (ou sem validação), qualquer requisição capturada pode ser repetida indefinidamente. Mesmo com TLS, logs, proxies e integrações podem expor requisições em algum ponto.

3) Truncar o tag sem critério

Reduzir o tamanho do HMAC pode ser aceitável para economizar bytes, mas precisa de análise. Tags muito curtos tornam ataques por tentativa e erro mais viáveis, especialmente em APIs expostas publicamente.

4) Reutilizar a mesma chave em muitos contextos

Usar uma única chave HMAC para múltiplos propósitos (por exemplo, autenticar webhooks e também assinar tokens internos) aumenta o impacto de um vazamento e dificulta rotação. Prefira chaves separadas por finalidade (separação de domínios).

5) Expor segredos no cliente

HMAC pressupõe segredo compartilhado. Se você coloca a chave em um aplicativo distribuído para usuários finais (mobile/desktop) sem proteção adequada, ela pode ser extraída. Nesse cenário, HMAC não autentica “o usuário”, apenas “quem conseguiu extrair a chave”. Para autenticação forte de usuários finais, normalmente você precisa de outro desenho (por exemplo, credenciais por usuário, hardware seguro, ou assinaturas com chaves privadas por dispositivo).

Assinaturas digitais: o que muda no fluxo prático

Quando você troca HMAC por assinatura digital, o fluxo conceitual é parecido (criar string para assinatura, assinar, verificar), mas com diferenças operacionais importantes:

  • Distribuição: o verificador precisa da chave pública do emissor (ou de um certificado/cadeia confiável), não de um segredo.
  • Rotação: você pode publicar novas chaves públicas e manter múltiplas chaves válidas durante transição.
  • Identidade: a assinatura pode ser vinculada a uma identidade (por exemplo, um certificado), facilitando auditoria.

Exemplo de cabeçalhos com assinatura

Um padrão comum é enviar:

  • Key-Id: identifica qual chave pública usar
  • Signature: assinatura do material canônico
  • Signed-Headers: lista do que foi assinado

O servidor então obtém a chave pública correspondente ao Key-Id e verifica a assinatura. Note que, ao contrário do HMAC, o servidor não ganha a capacidade de assinar em nome do emissor.

Critérios objetivos para escolher entre HMAC e assinaturas

Use HMAC quando

  • Há um relacionamento bilateral ou um domínio único de confiança (por exemplo, seus serviços internos, ou você e um provedor específico).
  • Você precisa de alto desempenho e baixa latência.
  • Não-repúdio não é requisito.
  • Você consegue gerenciar segredos com segurança (cofre de segredos, rotação, controle de acesso, auditoria).

Use assinaturas digitais quando

  • Você precisa que qualquer verificador valide sem receber um segredo.
  • Você quer separar quem assina de quem verifica (verificador não pode forjar).
  • Você precisa de melhor atribuição e evidência (não-repúdio, auditoria, conformidade).
  • Você tem múltiplos consumidores/verificadores distribuídos e quer simplificar a distribuição de confiança.

Checklist de implementação segura (HMAC)

  • Defina claramente o material assinado (método, path, query, timestamp, hash do corpo, nonce quando necessário).
  • Use uma biblioteca padrão para HMAC e para comparação em tempo constante.
  • Valide timestamp com janela curta e relógios razoavelmente sincronizados.
  • Considere replay: nonce/idempotência em operações críticas.
  • Separe chaves por finalidade e use KeyId para rotação.
  • Proteja a chave: armazenamento em cofre, mínimo privilégio, rotação e auditoria.
  • Log com cuidado: nunca registre a chave; evite registrar assinaturas completas em ambientes expostos, e trate headers sensíveis como secretos.

Agora responda o exercício sobre o conteúdo:

Em qual cenário a escolha por assinatura digital é mais apropriada do que HMAC, considerando o modelo de chaves e a necessidade de verificação por múltiplas partes?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Assinaturas digitais usam chave privada para assinar e chave publica para verificar, permitindo que muitos verificadores validem sem possuir um segredo e sem poder assinar. Isso melhora a separacao de papeis e a auditabilidade, ao contrario do HMAC com chave compartilhada.

Próximo capitúlo

Gestão de chaves e ciclo de vida: geração, armazenamento, rotação e revogação

Arrow Right Icon
Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.