hmac no Python: autentique mensagens

Publicado em: 19/08/2026
Tempo de leitura: 6 minutos
Tela de verificação de conta representando autenticação de mensagens com HMAC no Python

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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Leitor de impressão digital representando verificação de hashes com hashlib no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hashlib no Python: hashes seguros

    Aprenda hashlib no Python para calcular SHA-256, verificar arquivos, usar BLAKE2, derivar chaves e evitar erros comuns de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Código binário projetado representando conversões com binascii no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    binascii no Python: binário e ASCII

    Aprenda binascii no Python para converter hexadecimal, Base64 e quoted-printable, calcular CRC e validar dados binários com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    codecs no Python: domine encodings

    Aprenda codecs no Python para trabalhar com encodings, handlers de erro, BOM, streams incrementais e migrar codecs.open para open.

    Ler mais

    Tempo de leitura: 7 minutos
    18/08/2026
    A person reads 'Python for Unix and Linux System Administration' indoors.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    base64 no Python: codifique dados

    Aprenda base64 no Python para codificar bytes, usar Base64 URL-safe, validar padding, aplicar limites e diferenciar encoding de criptografia.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.parse no Python: manipule URLs

    Aprenda urllib.parse no Python para decompor URLs, criar queries, codificar componentes e evitar riscos com urljoin, redirects e SSRF.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ipaddress no Python: redes IPv4 e IPv6

    Aprenda ipaddress no Python para validar IPv4 e IPv6, calcular redes CIDR, dividir sub-redes e criar políticas de acesso seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026