---
title: "hashlib em Python: checksums, integridade e hashes seguros"
url: "https://python.dev.br/blog/python-hashlib-checksums-integridade/"
markdown_url: "https://python.dev.br/blog/python-hashlib-checksums-integridade.MD"
description: "Aprenda hashlib em Python para calcular SHA-256, verificar integridade de arquivos, assinar mensagens com HMAC e hash de senhas sem confiar em MD5."
date: "2026-07-29"
author: "Equipe Python Brasil"
---

# hashlib em Python: checksums, integridade e hashes seguros

Aprenda hashlib em Python para calcular SHA-256, verificar integridade de arquivos, assinar mensagens com HMAC e hash de senhas sem confiar em MD5.


O módulo `hashlib` é a ferramenta da biblioteca padrão para calcular **checksums, fingerprints e resumos criptográficos em Python**. Com ele você verifica se um arquivo baixado bate com o hash publicado, detecta alteração em payloads de webhook, gera chaves estáveis a partir de conteúdo e evita confiar cegamente em MD5.

A recomendação direta é: use `hashlib.sha256()` ou `hashlib.blake2b()` para integridade; alimente dados grandes com `update()` em blocos ou `file_digest()`; compare digests com `hmac.compare_digest()`; e nunca use hash rápido puro para senhas de usuário. Para tokens aleatórios, continue em [`secrets`](/blog/python-secrets-tokens-senhas-seguros/); para IDs públicos, use [`uuid`](/blog/python-uuid-identificadores-unicos/).

Neste guia, você vai calcular SHA-256 de strings e arquivos, montar verificação de download, assinar mensagens com HMAC, entender o papel do salt e separar três problemas diferentes: integridade, autenticação de mensagem e armazenamento de senha.

## O que um hash resolve — e o que ele não resolve

Uma função de hash mapeia dados de tamanho arbitrário para um resumo de tamanho fixo. Em SHA-256, o resumo tem 32 bytes (64 caracteres hexadecimais). A mesma entrada sempre produz a mesma saída. Uma mudança mínima no conteúdo muda o digest de forma imprevisível.

Isso é útil quando você precisa responder:

- este arquivo ainda é o mesmo que eu baixei ontem?
- o corpo do webhook chegou intacto?
- dois documentos são byte a byte iguais sem compará-los inteiros na rede?
- qual fingerprint curto eu gravo no banco para detectar reenvio?

Hash **não** é criptografia reversível. Você não “desfaz” um SHA-256 para obter o texto original. Hash também **não** autentica sozinho a origem da mensagem: se o atacante puder alterar o conteúdo e o hash ao mesmo tempo, a verificação passa. Para autenticar origem e integridade juntos, use HMAC com uma chave secreta, ou assinatura assimétrica.

## Como calcular SHA-256 de um texto

O caminho mais comum:

```python
import hashlib

mensagem = "pedido-1001|aprovado"
digest = hashlib.sha256(mensagem.encode("utf-8")).hexdigest()
print(digest)
```

Saída típica (o valor muda se o texto mudar):

```text
a3f1...  # 64 caracteres hexadecimais
```

Dois detalhes importantes:

1. `hashlib` trabalha com **bytes**, não com `str`. Por isso o `.encode("utf-8")`.
2. `hexdigest()` devolve texto legível; `digest()` devolve os 32 bytes brutos, melhores para armazenamento binário ou comparação interna.

Evite confiar em encoding implícito. Em sistemas brasileiros, nomes de arquivo, CPF formatado e textos com acentos precisam de uma política explícita de normalização antes do hash. Se um serviço hasheia `"José"` em UTF-8 e outro em Latin-1, os digests nunca batem.

## Hash incremental e arquivos grandes

Para arquivos, logs e streams, não carregue tudo na memória:

```python
from pathlib import Path
import hashlib

def sha256_arquivo(caminho: Path, bloco: int = 1024 * 1024) -> str:
    h = hashlib.sha256()
    with caminho.open("rb") as f:
        while True:
            pedaco = f.read(bloco)
            if not pedaco:
                break
            h.update(pedaco)
    return h.hexdigest()
```

A partir do Python 3.11 existe um atalho oficial:

```python
from pathlib import Path
import hashlib

with Path("relatorio.pdf").open("rb") as f:
    digest = hashlib.file_digest(f, "sha256").hexdigest()
```

`file_digest()` reduz boilerplate e deixa a intenção clara. Em workers que processam uploads de NFe, planilhas ou backups, essa leitura em blocos evita picos de memória e timeouts.

## Verificar integridade de um download

Um padrão clássico em repositórios e pacotes internos:

```python
from pathlib import Path
import hashlib
import hmac

def arquivo_integro(caminho: Path, esperado_hex: str) -> bool:
    with caminho.open("rb") as f:
        obtido = hashlib.file_digest(f, "sha256").hexdigest()
    return hmac.compare_digest(obtido.lower(), esperado_hex.lower())
```

Use `hmac.compare_digest` em vez de `==` quando o valor comparado for segredo ou quando a verificação fizer parte de um fluxo sensível. A comparação em tempo constante reduz uma classe de ataques de timing.

Em automação de CI, grave o hash esperado no manifesto do artefato e falhe o pipeline se o binário baixado divergir. Isso é especialmente útil em ambientes corporativos brasileiros que espelham pacotes internos ou imagens base.

## MD5 e SHA-1: legados, não padrão de segurança

Ainda existem sistemas que pedem MD5. Para compatibilidade com legado, o Python continua expondo o algoritmo:

```python
import hashlib

print(hashlib.md5(b"legado").hexdigest())
```

Trate MD5 e SHA-1 como **compatibilidade**, não como proteção. Existem colisões práticas: um atacante pode produzir dois arquivos diferentes com o mesmo MD5. Se o objetivo for segurança ou auditoria moderna, migre para SHA-256, SHA-512 ou BLAKE2.

Quando um parceiro ainda exige MD5 no layout de arquivo, calcule o MD5 exigido **e** um SHA-256 interno para a sua trilha de auditoria.

## BLAKE2 e escolha de algoritmo

Para fingerprints locais de alto desempenho, BLAKE2 costuma ser excelente:

```python
import hashlib

print(hashlib.blake2b(b"conteudo", digest_size=32).hexdigest())
```

Regras práticas:

- **SHA-256**: padrão intercambiável, amplo suporte em APIs, bancos e documentação.
- **SHA-512**: útil quando o ambiente já padroniza 512 bits.
- **BLAKE2b / BLAKE2s**: ótimo desempenho em software; ótimo para fingerprints internos.
- **SHA-3**: disponível, mas menos comum em integrações legadas.
- **MD5 / SHA-1**: só para interoperar com sistemas antigos.

Liste o que a instalação oferece com `hashlib.algorithms_guaranteed` e `hashlib.algorithms_available` se o código rodar em múltiplas plataformas.

## HMAC: integridade com chave secreta

Quando a pergunta muda de “o conteúdo mudou?” para “o conteúdo mudou **e** veio de quem conhece a chave?”, use HMAC:

```python
import hashlib
import hmac

chave = b"segredo-do-webhook"
corpo = b'{"evento":"pagamento.aprovado","id":"px_123"}'

assinatura = hmac.new(chave, corpo, hashlib.sha256).hexdigest()
print(assinatura)
```

Na validação:

```python
import hmac
import hashlib

def webhook_valido(chave: bytes, corpo: bytes, assinatura_recebida: str) -> bool:
    esperada = hmac.new(chave, corpo, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperada, assinatura_recebida)
```

Esse padrão aparece em webhooks de pagamento, filas internas e callbacks de CRM. A chave fica em variável de ambiente ou cofre; o corpo assinado é o payload bruto recebido, sem re-serializar o JSON de forma diferente da origem.

Para geração e rotação de chaves aleatórias, combine com o módulo [`secrets`](/blog/python-secrets-tokens-senhas-seguros/). Para configuração tipada de ambiente, veja também [`python-dotenv` e variáveis de ambiente](/blog/python-dotenv-env-vars-config/).

## Salt, pepper e o erro de hashear senha “no braço”

Um exemplo incompleto e **inadequado** para senhas reais:

```python
import hashlib

def hash_fraco(senha: str) -> str:
    return hashlib.sha256(senha.encode("utf-8")).hexdigest()
```

Problemas:

- o mesmo senha sempre gera o mesmo hash (fácil de correlacionar);
- SHA-256 é rápido demais para brute force com GPU;
- não há fator de custo configurável.

Mesmo com salt simples, hash rápido puro continua frágil para senhas de usuário. A stdlib oferece PBKDF2:

```python
import hashlib
import os
import secrets

def derivar_chave(senha: str, salt: bytes | None = None) -> tuple[bytes, bytes]:
    if salt is None:
        salt = secrets.token_bytes(16)
    chave = hashlib.pbkdf2_hmac(
        "sha256",
        senha.encode("utf-8"),
        salt,
        iterations=600_000,
    )
    return salt, chave
```

PBKDF2 com muitas iterações é aceitável em alguns contextos de derivação de chave, mas aplicações novas de autenticação de usuário devem preferir **Argon2id**, **scrypt** ou **bcrypt** via bibliotecas mantidas. Guarde salt por usuário, parâmetros do algoritmo e nunca o segredo em texto puro.

Resumo da separação:

| Necessidade | Ferramenta |
| --- | --- |
| Token de recuperação / API key aleatória | `secrets` |
| Fingerprint de arquivo ou payload | `hashlib` (SHA-256 / BLAKE2) |
| Webhook autenticado | `hmac` + `hashlib` |
| Senha de usuário | Argon2id / scrypt / bcrypt |
| Identificador público de recurso | `uuid` |

## Caso prático: manifesto de artefatos em pipeline

Imagine um job que gera um CSV de conciliação e publica o arquivo em object storage:

```python
from pathlib import Path
import hashlib
import json

def publicar_manifesto(arquivo: Path) -> dict:
    with arquivo.open("rb") as f:
        digest = hashlib.file_digest(f, "sha256").hexdigest()
    manifesto = {
        "nome": arquivo.name,
        "algoritmo": "sha256",
        "digest": digest,
        "tamanho": arquivo.stat().st_size,
    }
    Path(f"{arquivo}.sha256.json").write_text(
        json.dumps(manifesto, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    return manifesto
```

No consumidor:

1. baixe o CSV e o manifesto;
2. recalcule o SHA-256;
3. compare com `compare_digest`;
4. só então carregue o arquivo no pandas, DuckDB ou no banco.

Esse fluxo reduz o risco de processar um artefato truncado por falha de rede ou sobrescrito por deploy paralelo. Em times de dados e financeiro, a falha “silenciosa” de arquivo incompleto costuma ser mais cara do que o retry explícito.

## Erros comuns com hashlib

1. **Hashear `str` sem encoding explícito** — quebra entre sistemas.
2. **Usar MD5 “porque é mais curto”** — curto não é critério de segurança.
3. **Comparar digests com `==` em fluxos sensíveis** — prefira `compare_digest`.
4. **Recarregar JSON antes de validar HMAC** — a assinatura deve cobrir os bytes originais.
5. **Usar hash rápido para senha de usuário** — escolha algoritmo de senha de propósito.
6. **Logar digest de tokens de sessão como se fossem públicos** — fingerprint de segredo ainda pode vazar informação em alguns desenhos; minimize logs.
7. **Confiar no nome do arquivo em vez do conteúdo** — o hash fala do conteúdo; o nome não.

## Testes determinísticos

Hashes são determinísticos, o que facilita testes:

```python
import hashlib

def fingerprint_pedido(payload: str) -> str:
    return hashlib.sha256(payload.encode("utf-8")).hexdigest()


def test_fingerprint_estavel():
    assert fingerprint_pedido("a") == fingerprint_pedido("a")
    assert fingerprint_pedido("a") != fingerprint_pedido("b")
```

Para HMAC, injete a chave no teste. Para arquivos, use `tmp_path` do pytest e um conteúdo minúsculo. Evite depender de arquivos grandes do repositório só para validar o algoritmo.

## Quando hashlib não é a ferramenta certa

- **Criptografar** dados para depois recuperar: use bibliotecas de criptografia autenticada, não hash.
- **Gerar ID público de pedido**: prefira [`uuid`](/blog/python-uuid-identificadores-unicos/).
- **Gerar segredo adivinhável apenas por quem o possui**: use [`secrets`](/blog/python-secrets-tokens-senhas-seguros/).
- **Assinatura com par de chaves pública/privada**: use ecossistema de assinatura digital, não só HMAC simétrico.
- **Deduplicação semântica de texto** (“frases parecidas”): hash criptográfico exige igualdade exata de bytes; similaridade pede outra técnica.

## Conclusão e próximos passos

`hashlib` é pequeno, estável e suficiente para a maior parte das necessidades de **integridade e fingerprint** em Python. Padronize SHA-256 ou BLAKE2, leia arquivos em blocos, valide webhooks com HMAC e reserve algoritmos de senha modernos para credenciais de usuário.

Para continuar no mesmo eixo prático:

- [secrets em Python: tokens e senhas aleatórias](/blog/python-secrets-tokens-senhas-seguros/)
- [uuid em Python: identificadores únicos](/blog/python-uuid-identificadores-unicos/)
- [Segurança em aplicações Python](/blog/seguranca-em-aplicacoes-python/)
- [Variáveis de ambiente com python-dotenv](/blog/python-dotenv-env-vars-config/)
- [Subprocess e comandos externos](/blog/python-subprocess-comandos-externos/)

Se o seu time publica artefatos, processa webhooks ou confere downloads em automação, um helper de SHA-256 com `file_digest` e `compare_digest` costuma eliminar uma classe inteira de incidentes silenciosos.
