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

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026