HMAC é uma construção criptográfica que combina uma chave secreta com uma função hash para produzir um código de autenticação de mensagem. Diferentemente de um hash comum, o resultado só pode ser recalculado por quem conhece a chave. Isso permite verificar duas propriedades importantes: a mensagem não foi alterada e foi produzida por uma parte que possuía o segredo.
O módulo hmac da biblioteca padrão implementa o algoritmo definido no RFC 2104. Ele é útil para validar webhooks, autenticar mensagens entre serviços, proteger arquivos de configuração distribuídos, assinar cookies simples e verificar payloads recebidos por filas ou APIs. Este guia explica geração, validação, comparação segura, canonicalização, replay attacks e rotação de chaves.
Hash comum ou HMAC?
Um hash como SHA-256 não usa segredo. Qualquer pessoa que altere os dados pode calcular um novo hash. Por isso, um digest público detecta corrupção acidental ou confirma que um arquivo corresponde a um valor confiável, mas não autentica sozinho a origem.
HMAC adiciona uma chave ao cálculo por meio de uma construção padronizada. Não tente criar algo como sha256(secret + message) por conta própria. Construções caseiras podem sofrer problemas de extensão de comprimento, ambiguidades ou separação inadequada dos campos.
Para revisar hashes gerais, consulte o guia de hashlib no Python. Para chaves e tokens aleatórios, o artigo sobre segredos seguros no Python apresenta práticas complementares.
Criando um HMAC com SHA-256
import hmac
import hashlib
key = b"chave-secreta-gerada-aleatoriamente"
message = b"pedido=123&valor=49.90"
mac = hmac.new(key, message, digestmod=hashlib.sha256)
print(mac.hexdigest())
A chave e a mensagem precisam ser bytes-like. Para texto, defina explicitamente UTF-8:
key = "segredo do servidor".encode("utf-8")
message = "ação=confirmar".encode("utf-8")
O parâmetro digestmod é obrigatório. SHA-256 é uma escolha comum e interoperável. HMAC precisa de um hash com digest de tamanho fixo, portanto SHAKE-128 e SHAKE-256 não são compatíveis.
Atalho hmac.digest()
Quando toda a mensagem já está em memória, hmac.digest() oferece uma interface curta e pode usar uma implementação otimizada.
signature = hmac.digest(
key,
message,
"sha256",
)
print(signature.hex())
Para streams ou arquivos grandes, use um objeto HMAC e chame update() em blocos.
Processamento incremental
from pathlib import Path
import hmac
import hashlib
def hmac_file(path: Path, key: bytes) -> str:
mac = hmac.new(key, digestmod=hashlib.sha256)
with path.open("rb") as file:
while chunk := file.read(1024 * 1024):
mac.update(chunk)
return mac.hexdigest()
Esse padrão evita carregar arquivos grandes na memória. Ele segue as mesmas técnicas usadas para ler arquivos gigantes no Python.
Verificação com compare_digest()
Não compare tags secretas com ==. Uma implementação comum pode parar na primeira diferença, produzindo pequenas variações de tempo. Em cenários remotos, muitas medições podem revelar informação.
def verify_signature(
key: bytes,
message: bytes,
supplied_hex: str,
) -> bool:
expected = hmac.new(
key,
message,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, supplied_hex)
Os valores comparados devem ter o mesmo tipo. Para strings, use somente ASCII, como um digest hexadecimal. Diferenças de comprimento ainda podem revelar tamanho, então valide o formato esperado antes da comparação.
Validando um webhook
Um webhook costuma enviar o corpo bruto da requisição e uma assinatura em um cabeçalho. A regra mais importante é calcular o HMAC sobre exatamente os mesmos bytes usados pelo provedor.
def verify_webhook(body: bytes, header: str, secret: bytes) -> None:
if not header.startswith("sha256="):
raise ValueError("Formato de assinatura inválido")
supplied = header.removeprefix("sha256=")
if len(supplied) != 64:
raise ValueError("Tamanho de assinatura inválido")
expected = hmac.new(secret, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, supplied):
raise PermissionError("Assinatura inválida")
Não decodifique JSON e serialize novamente antes de verificar. Espaços, ordem das chaves, escapes e quebras de linha podem mudar os bytes. Leia o corpo bruto, valide a assinatura e só depois faça o parsing.
Canonicalização de mensagens
Quando você define o próprio protocolo, especifique exatamente os campos, a ordem, o encoding e os separadores. Concatenações ambíguas podem gerar a mesma sequência:
# Ambíguo: ("ab", "c") e ("a", "bc")
message = user_id + action
Uma alternativa é usar campos com comprimento explícito ou uma serialização canônica:
def canonical_message(timestamp: int, method: str, path: str, body: bytes) -> bytes:
prefix = f"v1\n{timestamp}\n{method.upper()}\n{path}\n".encode("utf-8")
return prefix + body
Inclua uma versão para permitir evolução futura sem confundir assinaturas antigas.
Proteção contra replay
Um HMAC válido não impede que um atacante capture e reenvie a mesma mensagem. Para comandos sensíveis, inclua timestamp, nonce ou identificador único no conteúdo autenticado.
import time
MAX_AGE = 300
if abs(time.time() - timestamp) > MAX_AGE:
raise PermissionError("Mensagem expirada")
Registre nonces já usados durante a janela aceita. A ordem correta é: validar estrutura e limites, verificar idade, calcular HMAC, comparar e somente então marcar o nonce e executar a ação.
Gerenciamento da chave
Uma chave HMAC deve possuir entropia suficiente e vir de uma fonte criptograficamente segura.
import secrets
key = secrets.token_bytes(32)
Não derive a chave de uma frase curta, não a coloque no repositório e não a registre em logs. Use variáveis de ambiente protegidas, secret managers ou arquivos com permissões restritas. O guia de variáveis de ambiente no Python ajuda a separar configuração de código.
Rotação de chaves
Associe cada chave a um identificador, como key_id. Durante uma rotação, gere assinaturas apenas com a chave nova, mas aceite temporariamente a anterior para mensagens criadas antes da mudança.
KEYS = {
"2026-08": b"nova-chave...",
"2026-07": b"chave-anterior...",
}
O identificador não é secreto, mas precisa ser validado e limitado. Nunca tente todas as chaves sem controle quando o atacante puder gerar muitas requisições.
Truncamento da tag
Alguns protocolos usam apenas parte do HMAC para economizar espaço. Isso reduz a resistência a falsificação. Prefira o digest completo, salvo quando um padrão confiável definir explicitamente o tamanho mínimo. Nunca compare uma tag fornecida com um prefixo sem primeiro exigir o tamanho correto.
HMAC em APIs
Assinaturas de API geralmente incluem método HTTP, caminho, query canônica, timestamp e hash do corpo. Todos esses elementos precisam ser normalizados de forma idêntica no cliente e no servidor. O guia de APIs REST com Python complementa timeout, validação e tratamento de respostas.
HMAC não criptografa
A mensagem continua visível. HMAC oferece integridade e autenticidade, não confidencialidade. Use TLS para proteger dados em trânsito e criptografia autenticada quando precisar armazenar conteúdo secreto. Não use a mesma chave para HMAC e criptografia; derive chaves separadas com uma KDF adequada.
Erros comuns
Os principais erros são comparar com ==, assinar JSON reserializado, omitir timestamp, aceitar assinatura com tamanho variável, usar chave curta e previsível, registrar segredos, reutilizar a mesma chave para finalidades diferentes e executar a ação antes da verificação.
Boas práticas
Use SHA-256, chaves aleatórias de pelo menos 32 bytes, uma mensagem canônica versionada e compare_digest(). Autentique timestamp e nonce, limite tamanhos antes de processar, faça rotação por identificador e registre somente metadados não sensíveis. Use bibliotecas oficiais do provedor quando o esquema de assinatura for complexo.
Conclusão
O módulo hmac fornece uma forma padronizada de autenticar mensagens usando uma chave secreta e uma função hash. A chamada é simples; a segurança depende dos bytes assinados, da comparação, do gerenciamento da chave e da proteção contra replay. Quando esses detalhes são especificados claramente, HMAC é uma ferramenta robusta para webhooks e comunicação entre serviços.
Consulte a documentação oficial do módulo hmac e o RFC 2104.







