HMAC em Python — hmac.new() SHA-256 com exemplos de código

·DevOps Engineer & Python Automation Specialist·Revisado porMaria Santos·Publicado

Use o Gerador HMAC gratuito diretamente no seu navegador — sem instalação.

Experimentar Gerador HMAC online →

Todo callback de webhook, toda requisição de API assinada, toda notificação de evento do Stripe ou GitHub usa uma assinatura HMAC para provar que o payload não foi adulterado. O módulo hmac do Python trata HMAC-SHA256 em Python com uma única chamada de função: hmac.new(key, msg, hashlib.sha256). Sem pip install, sem extensão C, sem dependência de terceiros. Para verificações rápidas de assinatura sem escrever código, o HMAC Generator online dá o resultado instantaneamente. Este guia abrange hmac.new(), hmac.digest(), hmac.compare_digest(), codificação Base64, verificação de webhooks, assinatura de requisições de API e todos os algoritmos de hash, de SHA-1 a SHA-512. Todos os exemplos têm como alvo o Python 3.7+.

  • hmac.new(key, msg, hashlib.sha256) é o ponto de entrada padrão — key e msg devem ser bytes, e digestmod é obrigatório desde o Python 3.4.
  • hmac.digest(key, msg, "sha256") é uma alternativa one-shot mais rápida, adicionada no Python 3.7 — retorna bytes brutos sem objeto intermediário.
  • Sempre verifique assinaturas com hmac.compare_digest() para evitar ataques de temporização — nunca use == para comparação de HMAC.
  • Codifique em Base64 a saída bruta de .digest() para cabeçalhos HTTP e assinaturas de webhook: base64.b64encode(h.digest()).
  • O módulo hmac aceita qualquer algoritmo do hashlib: sha1, sha256, sha384, sha512, md5, blake2b.

O que é HMAC?

HMAC (Hash-based Message Authentication Code) é uma construção definida na RFC 2104 que combina uma chave secreta com uma função de hash para produzir uma tag de autenticação de tamanho fixo. Ao contrário de um hash simples (que qualquer pessoa pode calcular), um HMAC requer conhecimento da chave secreta. Isso significa que você pode usá-lo para verificar tanto a integridade quanto a autenticidade de uma mensagem. Se mesmo um único byte da mensagem ou da chave mudar, a saída é completamente diferente. A construção funciona fazendo o hash da chave com XOR aplicado a duas constantes de padding diferentes (ipad e opad), envolvendo a mensagem entre duas operações de hash. O módulo hmac do Python implementa essa RFC diretamente.

Before · Python
After · Python
# Hash SHA-256 simples — sem chave secreta, qualquer pessoa pode calcular
hashlib.sha256(b"payment:9950:USD").hexdigest()
# "7a3b1c..."  (determinístico, público)
# HMAC-SHA256 — requer a chave secreta para produzir
hmac.new(b"api_secret", b"payment:9950:USD", hashlib.sha256).hexdigest()
# "e4f2a8..."  (apenas o detentor da chave pode calcular)

hmac.new() — O Ponto de Entrada da Biblioteca Padrão

O módulo hmac faz parte da biblioteca padrão do Python. Dois imports e você está pronto: import hmac, hashlib. As três funções principais são hmac.new() (cria um objeto HMAC), hmac.digest() (one-shot, Python 3.7+) e hmac.compare_digest() (comparação em tempo constante). Nenhum pip install necessário.

hmac.new(key, msg, digestmod) recebe três argumentos. Tanto key quanto msg devem ser objetos bytes-like ( bytes, bytearray ou memoryview). O argumento digestmod é obrigatório desde o Python 3.4 e aceita qualquer construtor do hashlib (como hashlib.sha256) ou um nome em string como "sha256".

Python 3.7+ — exemplo mínimo de HMAC-SHA256
import hmac
import hashlib

key = b"webhook_signing_key_2026"
message = b'{"event":"invoice.paid","invoice_id":"inv_8f3a","amount":19900}'

# Cria o objeto HMAC e obtém a assinatura em hex
signature = hmac.new(key, message, hashlib.sha256).hexdigest()
print(signature)
# "b4e74f6c9a1d3e5f8b2a7c0d4e6f1a3b5c7d9e0f2a4b6c8d0e1f3a5b7c9d0e2f"

O objeto HMAC expõe dois métodos de saída. .digest() retorna bytes brutos (32 bytes para SHA-256, 64 para SHA-512). .hexdigest() retorna uma string hex em letras minúsculas. A string hex é um str Python simples — sem necessidade de etapa de decodificação.

Python 3.7+ — .digest() vs .hexdigest()
import hmac
import hashlib

key = b"service_auth_key"
msg = b"GET /api/v2/orders 2026-03-28T14:30:00Z"

h = hmac.new(key, msg, hashlib.sha256)

raw_bytes = h.digest()
print(type(raw_bytes), len(raw_bytes))
# <class 'bytes'> 32

hex_string = h.hexdigest()
print(type(hex_string), len(hex_string))
# <class 'str'> 64

# Representam os mesmos dados — hex é apenas uma codificação em string dos bytes
assert raw_bytes.hex() == hex_string

Se sua chave ou mensagem for uma string Python, chame .encode() para convertê-la em bytes antes de passá-la ao hmac.new(). Isso pega quase todo mundo na primeira vez — strings do Python 3 são Unicode, não bytes, e o módulo hmac as recusa.

Python 3.7+ — codificando strings para bytes
import hmac
import hashlib

# Chave e mensagem em string — .encode() converte para bytes UTF-8
api_key = "sk_live_9f3a2b7c4d8e"
request_body = '{"customer_id":"cust_4421","plan":"enterprise"}'

signature = hmac.new(
    api_key.encode(),
    request_body.encode(),
    hashlib.sha256
).hexdigest()

print(signature)
# "3a9f1b..."  — saída consistente em string hex
Nota:O parâmetro digestmod não tem valor padrão desde o Python 3.4. Chamar hmac.new(key, msg) sem ele levanta TypeError. Antes do 3.4, o padrão era MD5, e foi exatamente por isso que os mantenedores do Python removeram o padrão — forçando uma escolha explícita e segura.

HMAC-SHA256 Base64, SHA-1, SHA-512 e MD5

A função hmac.new() funciona com qualquer algoritmo de hash disponível no hashlib. A maioria dos provedores de webhook e gateways de API usa HMAC-SHA256, mas você vai encontrar SHA-1 no OAuth 1.0a, SHA-512 em protocolos que o exigem, e MD5 em sistemas legados que ainda não foram atualizados.

HMAC-SHA256 com Saída em Base64

Muitos provedores de webhook enviam a assinatura como uma string codificada em Base64 em um cabeçalho HTTP. Para produzir o mesmo formato, passe os bytes brutos de .digest() para base64.b64encode().

Python 3.7+ — codificação HMAC-SHA256 em Base64
import hmac
import hashlib
import base64

key = b"whsec_MbkP7x9yFqHGn3tRdWz5"
payload = b'{"id":"evt_1Nq","type":"charge.succeeded","data":{"amount":4200}}'

# Digest bruto → Base64 (comum para cabeçalhos Authorization e assinaturas de webhook)
raw_digest = hmac.new(key, payload, hashlib.sha256).digest()
b64_signature = base64.b64encode(raw_digest).decode("ascii")

print(b64_signature)
# "dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="

# Este é o valor que você compararia contra o cabeçalho X-Signature
header_value = f"sha256={b64_signature}"
print(header_value)
# "sha256=dGhpcyBpcyBhIHNhbXBsZSBzaWduYXR1cmU="

HMAC-SHA1 — Compatibilidade com Protocolos Legados

SHA-1 é considerado fraco para novos projetos, mas HMAC-SHA1 ainda é exigido pelo OAuth 1.0a e algumas implementações mais antigas de webhook. O código é idêntico — basta trocar o algoritmo.

Python 3.7+ — HMAC-SHA1
import hmac
import hashlib

consumer_secret = b"oauth_consumer_secret_2026"
token_secret = b"oauth_token_secret_2026"

# OAuth 1.0a usa consumer_secret&token_secret como chave de assinatura
signing_key = consumer_secret + b"&" + token_secret
base_string = b"GET&https%3A%2F%2Fapi.service.com%2Fv1%2Forders&oauth_nonce%3D7f3a91bc"

sig = hmac.new(signing_key, base_string, hashlib.sha1).digest()

import base64
oauth_signature = base64.b64encode(sig).decode("ascii")
print(oauth_signature)
# "Tza3R9sE..."  — codifique em URL para o cabeçalho Authorization

HMAC-SHA512 — Saída Maior

Python 3.7+ — HMAC-SHA512
import hmac
import hashlib

key = b"high_security_signing_key_64_bytes_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
msg = b'{"transfer_id":"xfr_9c2e","amount":500000,"currency":"EUR"}'

h = hmac.new(key, msg, hashlib.sha512)

print(len(h.digest()))   # 64 bytes (512 bits)
print(len(h.hexdigest()))  # 128 caracteres hex
print(h.hexdigest()[:40] + "...")
# "8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f..."

HMAC-MD5 — Somente para Legado

Python 3.7+ — HMAC-MD5
import hmac
import hashlib

# MD5 é criptograficamente quebrado — use apenas para compatibilidade com protocolos legados
key = b"legacy_api_key"
msg = b"action=charge&amount=1500&merchant=store_42"

sig = hmac.new(key, msg, hashlib.md5).hexdigest()
print(sig)  # string hex de 32 caracteres
# "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
Aviso:HMAC-MD5 é aceitável apenas para compatibilidade retroativa com sistemas que você não pode migrar. Para qualquer projeto novo, use HMAC-SHA256 no mínimo. MD5 possui vulnerabilidades de colisão conhecidas que, embora menos diretamente exploráveis no modo HMAC, tornam-no uma escolha inadequada como padrão.

Referência de Parâmetros do hmac.new()

A assinatura do construtor é hmac.new(key, msg=None, digestmod). As três funções do módulo compartilham o mesmo padrão para argumentos de chave e algoritmo.

Construtor hmac.new()

Parâmetro
Tipo
Padrão
Descrição
key
bytes | bytearray
(obrigatório)
A chave secreta — deve ser bytes, não uma string
msg
bytes | None
None
Mensagem inicial para assinar; mais dados podem ser adicionados via .update()
digestmod
str | callable
(obrigatório)
Algoritmo de hash — ex.: hashlib.sha256 ou a string "sha256"

hmac.digest() one-shot (Python 3.7+)

Parâmetro
Tipo
Descrição
key
bytes
A chave secreta
msg
bytes
A mensagem a autenticar
digest
str | callable
Algoritmo de hash — igual a digestmod em hmac.new()

O parâmetro digestmod aceita um callable (como hashlib.sha256) ou um nome em string (como "sha256"). A forma com callable é preferida porque é validada no momento do import — um erro de digitação na forma string só falha em tempo de execução.

hmac.digest() — HMAC One-Shot Rápido (Python 3.7+)

O Python 3.7 adicionou hmac.digest(key, msg, digest) como uma função de nível de módulo. Ela calcula o HMAC em uma única chamada sem criar um objeto HMAC intermediário. O valor retornado são bytes brutos (equivalente a chamar .digest() no objeto). Essa função usa uma implementação C otimizada no CPython e evita a sobrecarga de alocação de objeto, tornando-a visivelmente mais rápida em loops densos.

Python 3.7+ — hmac.digest() one-shot
import hmac
import hashlib

key = b"batch_signing_key_2026"
messages = [
    b'{"order_id":"ord_001","total":4500}',
    b'{"order_id":"ord_002","total":8900}',
    b'{"order_id":"ord_003","total":2200}',
]

# Digest one-shot — sem objeto HMAC intermediário
signatures = [hmac.digest(key, msg, hashlib.sha256) for msg in messages]

# Converte para hex para exibição
for msg, sig in zip(messages, signatures):
    print(f"{msg[:30]}... -> {sig.hex()[:24]}...")

A limitação: hmac.digest() retorna apenas bytes brutos. Se você precisar da string hex diretamente, ainda é necessário usar hmac.new() pelo seu método .hexdigest(), ou encadear .hex() no resultado em bytes.

Nota:hmac.digest() não suporta chamadas incrementais a .update(). Se você estiver lendo um arquivo grande em partes e precisar calcular o HMAC do conteúdo, use hmac.new() e chame .update(chunk) em um loop.

Verificar Assinatura HMAC de Webhook e Resposta de API

O uso mais comum de HMAC em Python é a verificação de assinaturas de webhook. Todo grande provedor (Stripe, GitHub, Shopify, Twilio) assina payloads com HMAC-SHA256 e envia a assinatura em um cabeçalho. O padrão é sempre o mesmo: recalcule o HMAC sobre o corpo bruto da requisição, depois compare com hmac.compare_digest().

Verificação de Assinatura de Webhook

Python 3.7+ — verificação de HMAC de webhook (Flask)
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = b"whsec_MbkP7x9yFqHGn3tRdWz5"

@app.route("/webhooks/payments", methods=["POST"])
def handle_payment_webhook():
    # Obtém o corpo bruto — deve corresponder exatamente ao que foi assinado
    raw_body = request.get_data()

    # Obtém a assinatura do cabeçalho
    received_sig = request.headers.get("X-Signature-256", "")

    # Recalcula o HMAC sobre o corpo bruto
    expected_sig = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest()

    # Comparação em tempo constante — evita ataques de temporização
    if not hmac.compare_digest(f"sha256={expected_sig}", received_sig):
        abort(403, "Assinatura inválida")

    # Assinatura verificada — processa o evento
    event = request.get_json()
    print(f"Evento verificado: {event['type']} para {event['data']['amount']}")
    return "", 200

A função hmac.compare_digest() compara duas strings ou sequências de bytes em tempo constante. Uma comparação regular com == faz curto-circuito no primeiro byte divergente. Um atacante pode medir o tempo de resposta em muitas requisições e reconstruir gradualmente a assinatura esperada byte por byte. A comparação em tempo constante elimina esse canal lateral.

Verificação de Webhook do GitHub

O formato de webhook do GitHub ilustra o padrão completo. Ele envia um cabeçalho X-Hub-Signature-256 contendo sha256= seguido do HMAC-SHA256 em hex do corpo bruto da requisição, assinado com o segredo de webhook configurado nas configurações do seu repositório. A diferença principal em relação à verificação genérica de webhook é que você deve remover o prefixo sha256= antes de comparar, e deve ler os bytes brutos do corpo da requisição — fazer o parse do JSON antes disso muda a representação em bytes e quebra a verificação.

Python 3.7+ — verificação do X-Hub-Signature-256 do GitHub
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
GITHUB_WEBHOOK_SECRET = b"your_github_webhook_secret"

@app.route("/webhooks/github", methods=["POST"])
def handle_github_webhook():
    # GitHub envia: X-Hub-Signature-256: sha256=<hex_digest>
    sig_header = request.headers.get("X-Hub-Signature-256", "")

    if not sig_header.startswith("sha256="):
        abort(403, "Cabeçalho de assinatura ausente ou malformado")

    received_hex = sig_header[len("sha256="):]
    raw_body = request.get_data()  # bytes brutos — não faça parse do JSON antes disso

    expected_hex = hmac.new(
        GITHUB_WEBHOOK_SECRET, raw_body, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected_hex, received_hex):
        abort(403, "Assinatura não corresponde — o payload pode ter sido adulterado")

    event_type = request.headers.get("X-GitHub-Event", "unknown")
    payload = request.get_json()
    print(f"Evento GitHub {event_type} verificado: {payload.get('action', '')}")
    return "", 200

O mesmo padrão se aplica ao Shopify (X-Shopify-Hmac-SHA256) e ao Twilio (X-Twilio-Signature), com a única diferença sendo o nome do cabeçalho e se a assinatura está em hex ou codificada em Base64. Sempre verifique a documentação do provedor para confirmar o formato de codificação — misturar hex e Base64 é a causa mais comum de erros de assinatura não correspondente.

Autenticação de Requisições de API com HMAC

Algumas APIs exigem que o cliente assine cada requisição com HMAC. A string assinada geralmente inclui o método HTTP, o caminho, o timestamp e o corpo da requisição. Aqui está um padrão que uso para autenticação interna entre serviços.

Python 3.7+ — assinando requisições de API com HMAC-SHA256
import hmac
import hashlib
import time
import json

def sign_request(secret: bytes, method: str, path: str, body: str) -> dict:
    """Gera assinatura HMAC-SHA256 para uma requisição de API."""
    timestamp = str(int(time.time()))

    # Constrói a string de assinatura — método + caminho + timestamp + corpo
    signing_string = f"{method}\n{path}\n{timestamp}\n{body}"

    signature = hmac.new(
        secret,
        signing_string.encode(),
        hashlib.sha256
    ).hexdigest()

    return {
        "X-Timestamp": timestamp,
        "X-Signature": signature,
    }

# Uso
api_secret = b"sk_hmac_9f3a2b7c4d8e1a6f"
body = json.dumps({"customer_id": "cust_4421", "action": "suspend_account"})

headers = sign_request(api_secret, "POST", "/api/v2/customers/actions", body)
print(headers)
# {"X-Timestamp": "1711612200", "X-Signature": "a3f1b9c0..."}

Assinando Requisições HTTP com a Biblioteca requests

Python 3.7+ — requisições HMAC assinadas com a biblioteca requests
import hmac
import hashlib
import time
import json
import requests

API_SECRET = b"sk_hmac_9f3a2b7c4d8e1a6f"
BASE_URL = "https://api.billing-service.internal"

def make_signed_request(method: str, path: str, payload: dict) -> requests.Response:
    body = json.dumps(payload, separators=(",", ":"))  # JSON compacto, determinístico
    timestamp = str(int(time.time()))

    signing_string = f"{method}\n{path}\n{timestamp}\n{body}"
    signature = hmac.new(API_SECRET, signing_string.encode(), hashlib.sha256).hexdigest()

    headers = {
        "Content-Type": "application/json",
        "X-Timestamp": timestamp,
        "X-Signature": f"hmac-sha256={signature}",
    }

    try:
        return requests.request(method, f"{BASE_URL}{path}", data=body, headers=headers)
    except requests.RequestException as e:
        raise RuntimeError(f"Requisição assinada falhou: {e}") from e

# Envia uma requisição POST assinada
resp = make_signed_request("POST", "/api/v2/invoices", {
    "customer_id": "cust_4421",
    "line_items": [
        {"description": "Plano Pro - Março 2026", "amount": 4900},
        {"description": "Assentos extras (3)", "amount": 2100},
    ],
})
print(resp.status_code, resp.json())

Observação rápida: use separators=(",", ":") ao serializar o corpo para assinatura. O json.dumps() padrão adiciona espaços após os separadores, o que muda a representação em bytes e quebra a verificação de assinatura se o servidor serializar de forma diferente. JSON compacto dá uma forma canônica.

Geração de HMAC pela Linha de Comando

Às vezes você precisa calcular um HMAC sem escrever um script. O flag -c do Python e o openssl resolvem isso pelo terminal.

bash — HMAC-SHA256 via one-liner do Python
python3 -c "
import hmac, hashlib
print(hmac.new(b'my_secret', b'message_to_sign', hashlib.sha256).hexdigest())
"
# saída: string hex de 64 caracteres
bash — HMAC-SHA256 via openssl (para comparação)
echo -n "message_to_sign" | openssl dgst -sha256 -hmac "my_secret"
# SHA2-256(stdin)= 7d11...

# Passa um arquivo pelo HMAC do openssl
openssl dgst -sha256 -hmac "my_secret" < payload.json
bash — HMAC a partir de variável de ambiente
# Armazena a chave em variável de ambiente para evitar exposição no histórico do shell
export HMAC_KEY="sk_live_9f3a2b"
echo -n '{"event":"test"}' | python3 -c "
import hmac, hashlib, sys, os
key = os.environ['HMAC_KEY'].encode()
msg = sys.stdin.buffer.read()
print(hmac.new(key, msg, hashlib.sha256).hexdigest())
"
Nota:O flag echo -n é essencial — sem ele, o echo acrescenta um caractere de nova linha à mensagem, o que altera a saída do HMAC. Essa é a causa mais comum de assinatura não correspondente ao depurar pelo terminal.

Alternativa de Alto Desempenho — Biblioteca cryptography

Para a maioria das aplicações, o módulo hmac padrão é rápido o suficiente. Se você já usa a biblioteca cryptography para TLS ou manipulação de certificados, ela também oferece HMAC com OpenSSL por baixo. A principal diferença prática em relação à stdlib é a API .verify() baseada em exceção descrita a seguir — ela levanta uma exceção em caso de divergência em vez de retornar um booleano que você pode esquecer de verificar.

bash — instalar cryptography
pip install cryptography
Python 3.7+ — HMAC via biblioteca cryptography
from cryptography.hazmat.primitives import hashes, hmac as crypto_hmac

key = b"webhook_signing_key_2026"
message = b'{"event":"subscription.renewed","plan":"enterprise"}'

h = crypto_hmac.HMAC(key, hashes.SHA256())
h.update(message)
signature = h.finalize()  # bytes brutos

print(signature.hex())
# "9c4e2a..."

# Modo de verificação — levanta InvalidSignature em caso de divergência
h_verify = crypto_hmac.HMAC(key, hashes.SHA256())
h_verify.update(message)
h_verify.verify(signature)  # levanta cryptography.exceptions.InvalidSignature se errado

O método .verify() da biblioteca cryptography é particularmente útil: ele levanta uma exceção em caso de divergência em vez de retornar um booleano. Isso torna mais difícil ignorar acidentalmente uma falha de verificação. O hmac.compare_digest() da biblioteca padrão retorna True/ False, que pode ser silenciosamente ignorado se o desenvolvedor esquecer de verificar o valor de retorno.

Saída no Terminal com Realce de Sintaxe

Se você está depurando assinaturas HMAC no terminal e quer saída colorida, rich resolve isso bem.

bash — instalar rich
pip install rich
Python 3.7+ — saída HMAC colorida com rich
import hmac
import hashlib
from rich.console import Console
from rich.table import Table

console = Console()

key = b"debug_signing_key"
messages = {
    "/api/v2/orders": b'{"status":"active"}',
    "/api/v2/invoices": b'{"status":"pending"}',
    "/api/v2/customers": b'{"status":"verified"}',
}

table = Table(title="Assinaturas HMAC-SHA256")
table.add_column("Endpoint", style="cyan")
table.add_column("Assinatura (primeiros 32 chars)", style="green")

for endpoint, body in messages.items():
    sig = hmac.new(key, body, hashlib.sha256).hexdigest()
    table.add_row(endpoint, sig[:32] + "...")

console.print(table)
Nota:Rich é apenas para exibição no terminal. Não o use ao escrever assinaturas HMAC em arquivos, cabeçalhos HTTP ou sistemas de agregação de logs — os códigos de escape ANSI irão corromper a saída.

Trabalhando com Arquivos Grandes — HMAC Incremental

Para arquivos acima de 50 MB aproximadamente, carregar tudo na memória só para calcular um HMAC é desperdício. O método .update() no objeto HMAC permite alimentar dados em partes. Isso mantém o uso de memória constante independentemente do tamanho do arquivo.

Python 3.7+ — HMAC de arquivo grande em partes
import hmac
import hashlib

def hmac_file(key: bytes, filepath: str, chunk_size: int = 8192) -> str:
    """Calcula HMAC-SHA256 de um arquivo sem carregá-lo inteiramente na memória."""
    h = hmac.new(key, digestmod=hashlib.sha256)

    try:
        with open(filepath, "rb") as f:
            while True:
                chunk = f.read(chunk_size)
                if not chunk:
                    break
                h.update(chunk)
    except OSError as e:
        raise OSError(f"Não é possível ler o arquivo '{filepath}': {e}") from e

    return h.hexdigest()

# Assina um export de banco de dados de 2 GB
signing_key = b"backup_integrity_key_2026"
signature = hmac_file(signing_key, "/var/backups/db-export-2026-03.sql.gz")
print(f"HMAC-SHA256: {signature}")
Python 3.7+ — verifica assinatura HMAC de arquivo baixado
import hmac
import hashlib

def verify_file_hmac(key: bytes, filepath: str, expected_hex: str) -> bool:
    """Verifica a assinatura HMAC-SHA256 de um arquivo."""
    h = hmac.new(key, digestmod=hashlib.sha256)

    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            h.update(chunk)

    return hmac.compare_digest(h.hexdigest(), expected_hex)

# Verifica um artefato baixado
is_valid = verify_file_hmac(
    key=b"release_signing_key",
    filepath="/tmp/release-v3.2.0.tar.gz",
    expected_hex="8e3a1f9b2c4d6e7f0a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f",
)
print(f"Integridade do arquivo: {'válido' if is_valid else 'CORROMPIDO'}")
Nota:Mude para HMAC incremental quando o arquivo ultrapassar 50-100 MB ou ao processar uploads em um servidor web onde a memória por requisição importa. A abordagem com .update() usa um chunk_size fixo de memória independentemente do tamanho do arquivo. Uso 64 KB por padrão — grande o suficiente para amortizar o overhead de syscall, pequeno o suficiente para caber no cache L2 da maioria dos processadores.

Gerar uma Chave HMAC Criptograficamente Segura em Python

Uma chave fraca ou previsível compromete toda a construção HMAC. O módulo secrets (Python 3.6+) fornece bytes aleatórios criptograficamente fortes. Para HMAC-SHA256, use uma chave de 32 bytes. Para HMAC-SHA512, use 64 bytes. Esses valores correspondem ao tamanho do bloco interno dos respectivos algoritmos de hash, que é o comprimento de chave ideal segundo a RFC 2104.

Python 3.7+ — gerar chaves HMAC com secrets
import secrets

# Gera chaves correspondendo ao tamanho do bloco do algoritmo de hash
sha256_key = secrets.token_bytes(32)   # 256 bits — para HMAC-SHA256
sha512_key = secrets.token_bytes(64)   # 512 bits — para HMAC-SHA512

# Representação hex — segura para arquivos de configuração e variáveis de ambiente
print(f"Chave HMAC-SHA256: {sha256_key.hex()}")
# ex.: "a3f1b9c04e7d2f8a1b3c5d7e9f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f"

print(f"Chave HMAC-SHA512: {sha512_key.hex()}")
# string hex de 128 caracteres

# Base64 URL-safe — compacta, segura para cabeçalhos HTTP
import base64
b64_key = base64.urlsafe_b64encode(sha256_key).decode("ascii")
print(f"Chave Base64: {b64_key}")
# ex.: "o_G5wE59L4obPF1-nwortG2ODwobPExdXnqLnA0dLi8="
Aviso:Nunca use random.random() ou random.randbytes() do módulo random para chaves HMAC. O módulo random padrão usa o PRNG Mersenne Twister, que é previsível após observar 624 saídas. Sempre use secrets.token_bytes() para aleatoriedade sensível à segurança.

Comprimento de Chave e Requisitos da RFC 2104

A RFC 2104 especifica que a chave HMAC pode ter qualquer comprimento, mas recomenda uma chave de pelo menos L bytes — onde L é o comprimento da saída da função de hash subjacente. Para HMAC-SHA256, são 32 bytes (256 bits). Chaves menores que L bits reduzem a margem de segurança proporcionalmente. Chaves maiores que o tamanho do bloco do hash (64 bytes para SHA-256, 128 bytes para SHA-512) são primeiro reduzidas por hash ao tamanho do bloco, então não há benefício em usar chaves maiores que o tamanho do bloco. Use 32 bytes para HMAC-SHA256 e 64 bytes para HMAC-SHA512.

Armazenamento Seguro e Rotação de Chaves

Nunca codifique chaves HMAC diretamente no código-fonte. A abordagem padrão para implantações em produção é carregar a chave de uma variável de ambiente na inicialização: os.environ["HMAC_SECRET"].encode(). Para ambientes de maior segurança, armazene as chaves em um sistema de gerenciamento de segredos como AWS Secrets Manager, HashiCorp Vault ou GCP Secret Manager e as recupere em tempo de execução. Esses sistemas fornecem logs de auditoria, controles de acesso e rotação automática sem exigir um novo deploy de código.

Planeje a rotação de chaves desde o início. Quando uma chave é rotacionada, há uma janela em que requisições em trânsito foram assinadas com a chave antiga e falharão na verificação com a nova. A mitigação padrão é um breve período de sobreposição: aceite assinaturas tanto da chave antiga quanto da nova por um curto período (minutos a horas), depois retire a chave antiga. Se uma chave for comprometida — exposta em logs, vazada por um commit no git ou divulgada em um incidente — rotacione imediatamente e trate todas as assinaturas produzidas com a chave comprometida como não confiáveis. Re-verifique quaisquer resultados de verificação em cache e notifique os consumidores dependentes sobre a mudança de chave.

Usando bytearray e memoryview com hmac.new()

A função hmac.new() aceita qualquer objeto bytes-like tanto para o parâmetro key quanto para msg. Isso significa que você pode passar bytes, bytearray ou memoryview diretamente, sem copiar ou converter. Isso importa principalmente em dois cenários: implementações de protocolo de rede onde socket.recv_into() escreve dados em um buffer bytearray pré-alocado, e sistemas de alta vazão onde evitar cópias intermediárias reduz a pressão no GC. Um slice de memoryview é zero-copy: expõe uma janela no buffer original sem alocar nova memória. Em dezenas de milhares de mensagens por segundo, eliminar essas alocações faz uma diferença mensurável na latência e na vazão.

Python 3.7+ — bytearray e memoryview com HMAC
import hmac
import hashlib

# bytearray — bytes mutáveis, útil para buffers de protocolo binário
key = bytearray(b"protocol_signing_key")
frame = bytearray(b'\x01\x02\x03\x04payload_data_here')

sig = hmac.new(key, frame, hashlib.sha256).hexdigest()
print(f"Assinatura do frame: {sig[:32]}...")

# memoryview — slice zero-copy de um buffer maior
large_buffer = bytearray(4096)
large_buffer[:20] = b"sensor_reading_12345"

# HMAC apenas dos primeiros 20 bytes sem copiar
view = memoryview(large_buffer)[:20]
sig = hmac.new(key, view, hashlib.sha256).hexdigest()
print(f"Assinatura do sensor: {sig[:32]}...")

Erros Comuns

Vejo os dois primeiros erros em quase toda revisão de código envolvendo handlers de webhook. São fáceis de introduzir sob pressão de tempo e difíceis de detectar sem saber o que procurar.

Comparar assinaturas HMAC com == em vez de hmac.compare_digest()

Problema: O operador == faz curto-circuito no primeiro byte divergente, vazando informações de temporização que permitem a um atacante reconstruir a assinatura esperada incrementalmente.

Solução: Sempre use hmac.compare_digest() para comparação de assinaturas — ela roda em tempo constante independentemente de onde ocorre a divergência.

Before · Python
After · Python
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()

if received_sig == expected_sig:  # VULNERÁVEL a ataque de temporização
    process_webhook(body)
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()

if hmac.compare_digest(received_sig, expected_sig):  # tempo constante
    process_webhook(body)
Passar uma string em vez de bytes para hmac.new()

Problema: hmac.new() exige objetos bytes-like. Passar um str do Python levanta TypeError: "key: expected bytes or bytearray, but got 'str'".

Solução: Chame .encode() em chaves e mensagens em string antes de passá-las para hmac.new().

Before · Python
After · Python
key = "my_api_secret"  # str, não bytes
msg = '{"event":"test"}'  # str, não bytes
sig = hmac.new(key, msg, hashlib.sha256)  # TypeError!
key = "my_api_secret"
msg = '{"event":"test"}'
sig = hmac.new(key.encode(), msg.encode(), hashlib.sha256)
Esquecer digestmod (Python 3.4+)

Problema: Chamar hmac.new(key, msg) sem o terceiro argumento levanta TypeError. Antes do Python 3.4 o padrão era MD5, mas o padrão foi removido por razões de segurança.

Solução: Sempre passe o algoritmo explicitamente: hashlib.sha256, hashlib.sha512 ou o que seu protocolo exigir.

Before · Python
After · Python
# digestmod ausente — levanta TypeError no Python 3.4+
sig = hmac.new(key, msg).hexdigest()
# Sempre especifique o algoritmo de hash
sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
Usar .hexdigest() quando o provedor espera Base64

Problema: Muitos provedores de webhook (Stripe, Shopify) enviam assinaturas em Base64, não em hex. Comparar uma string hex com um valor Base64 sempre falha, fazendo todos os webhooks serem rejeitados.

Solução: Verifique a documentação do provedor para o formato de assinatura. Se usarem Base64, codifique os bytes brutos de .digest(), não a string de .hexdigest().

Before · Python
After · Python
# Provedor envia Base64, mas calculamos hex — nunca corresponde
expected = hmac.new(key, body, hashlib.sha256).hexdigest()
# "a3f1b9c0..."  vs  "o/G5wE59..."  — sempre diverge
import base64
# Corresponde ao formato do provedor: bytes brutos → Base64
raw = hmac.new(key, body, hashlib.sha256).digest()
expected = base64.b64encode(raw).decode("ascii")
# "o/G5wE59..."  — corresponde ao cabeçalho

stdlib hmac vs cryptography — Comparação Rápida

Método
Algoritmo
Saída
Streaming
Tipos Personalizados
Requer Instalação
hmac.new() + hexdigest()
Qualquer hashlib
String hex
✓ via .update()
N/A
Não (stdlib)
hmac.new() + digest()
Qualquer hashlib
Bytes brutos
✓ via .update()
N/A
Não (stdlib)
hmac.digest()
Qualquer hashlib
Bytes brutos
✗ (one-shot)
N/A
Não (stdlib, 3.7+)
hashlib.sha256 (hash simples)
SHA-256 apenas
Hex ou bytes
✓ via .update()
N/A
Não (stdlib)
cryptography (HMAC)
Qualquer
Bytes brutos
✓ via .update()
N/A
pip install
pyca/cryptography + CMAC
AES-CMAC
Bytes brutos
✓ via .update()
N/A
pip install

Use o módulo hmac da stdlib para verificação de webhooks, assinatura de API e operações HMAC gerais — não requer dependências e cobre todos os algoritmos padrão. Use hmac.digest() para operações em lote onde a velocidade one-shot importa. Recorra à biblioteca cryptography apenas quando você já depende dela para outras operações (TLS, X.509, criptografia simétrica) e deseja a API .verify() baseada em exceção. Para verificações rápidas de assinatura sem escrever Python, use a ferramenta HMAC Generator para colar sua chave e mensagem e obter o resultado instantaneamente.

Perguntas Frequentes

Qual é a diferença entre hmac.new() e hmac.digest() em Python?

hmac.new() retorna um objeto HMAC que suporta chamadas incrementais a .update() e oferece tanto .digest() (bytes brutos) quanto .hexdigest() (string hex). hmac.digest() é uma função one-shot adicionada no Python 3.7 que retorna bytes brutos diretamente, sem criar um objeto intermediário. Use hmac.digest() quando tiver a mensagem completa e precisar apenas do resultado. Use hmac.new() quando precisar alimentar dados em partes ou precisar da saída em hex.

Python
import hmac, hashlib

key = b"webhook_secret_2026"
body = b'{"event":"payment.completed","amount":9950}'

# One-shot — retorna bytes brutos
raw = hmac.digest(key, body, hashlib.sha256)

# Baseado em objeto — suporta atualizações incrementais e saída hex
h = hmac.new(key, body, hashlib.sha256)
hex_sig = h.hexdigest()

Como verifico uma assinatura HMAC em Python?

Recalcule o HMAC sobre a mensagem original usando o segredo compartilhado, depois compare com hmac.compare_digest(). Nunca use == para comparação de assinaturas. O operador == é vulnerável a ataques de temporização porque faz curto-circuito no primeiro byte divergente, vazando informações sobre o comprimento e conteúdo da assinatura esperada.

Python
import hmac, hashlib

def verify_signature(secret: bytes, message: bytes, received_sig: str) -> bool:
    expected = hmac.new(secret, message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_sig)

HMAC SHA-256 em Python é igual a um hash com hashlib.sha256?

Não. hashlib.sha256 calcula um hash SHA-256 simples da entrada, que qualquer pessoa pode reproduzir. HMAC-SHA256 mistura uma chave secreta ao cálculo do hash seguindo o RFC 2104, então apenas quem possui a chave consegue produzir ou verificar a saída correta. Um hash simples prova a integridade dos dados. Um HMAC prova tanto a integridade quanto a autenticidade.

Python
import hmac, hashlib

msg = b"transfer:9950:USD"
key = b"api_secret_k8x2"

plain_hash = hashlib.sha256(msg).hexdigest()  # qualquer pessoa pode calcular isso
hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest()  # requer a chave

Posso usar HMAC-SHA1 no Python 3?

Sim, basta passar hashlib.sha1 como argumento digestmod. HMAC-SHA1 ainda funciona normalmente no Python 3 e o módulo hmac não emite alertas de descontinuação para ele. Dito isso, SHA-1 é considerado fraco para novos projetos — sua resistência a colisões está abaixo de 80 bits e o NIST o descontinuou para a maioria dos usos de assinatura digital em 2015. O principal motivo para usar HMAC-SHA1 hoje é compatibilidade retroativa com protocolos existentes que o exigem, como OAuth 1.0a ou certos sistemas legados de webhook. Quando você controla ambos os lados da integração, prefira HMAC-SHA256 ou HMAC-SHA512 em todo trabalho novo.

Python
import hmac, hashlib

key = b"oauth_consumer_secret"
base_string = b"GET&https%3A%2F%2Fapi.example.com&oauth_nonce%3Dabc123"
sig = hmac.new(key, base_string, hashlib.sha1).digest()

Como gero uma chave HMAC segura em Python?

Use secrets.token_bytes() da biblioteca padrão. Para HMAC-SHA256, uma chave de 32 bytes é a recomendação padrão, pois corresponde ao tamanho do bloco do hash. Para HMAC-SHA512, use 64 bytes. Não use random.random() ou os.urandom() para geração de chaves em código de aplicação — secrets é o módulo correto para aleatoriedade sensível à segurança desde o Python 3.6.

Python
import secrets

hmac_sha256_key = secrets.token_bytes(32)  # 256 bits
hmac_sha512_key = secrets.token_bytes(64)  # 512 bits

# Armazene como hex para arquivos de configuração
print(hmac_sha256_key.hex())
# ex.: "a3f1b9c04e..."

Por que hmac.new() exige digestmod no Python 3?

Antes do Python 3.4, o digestmod padrão era MD5, que é criptograficamente quebrado — MD5 possui ataques de colisão conhecidos e nunca deve ser usado em código novo sensível à segurança. Os mantenedores do Python removeram o padrão para forçar uma escolha explícita de algoritmo, evitando que desenvolvedores entregassem MACs baseados em MD5 sem perceber. Se você chamar hmac.new(key, msg) sem digestmod, receberá um TypeError. Sempre passe o algoritmo explicitamente: hashlib.sha256, hashlib.sha512 ou qualquer outro construtor do hashlib. Na dúvida, hashlib.sha256 é o padrão seguro — sem vulnerabilidades conhecidas e rápido o suficiente para qualquer carga de trabalho prática.

Python
import hmac, hashlib

key = b"secret"
msg = b"data"

# Isso levanta TypeError no Python 3.4+
# hmac.new(key, msg)  # TypeError: missing required argument: 'digestmod'

# Sempre especifique o algoritmo
h = hmac.new(key, msg, hashlib.sha256)

Para uma verificação rápida de HMAC sem precisar rodar um script Python, cole sua chave e mensagem no HMAC Generator online — suporta SHA-256, SHA-384 e SHA-512 com resultados instantâneos.

Ferramentas Relacionadas

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.

MS
Maria SantosRevisor técnico

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.