base64 em Python: codificar, decodificar e transportar dados binários
Aprenda base64 em Python para codificar bytes, embutir imagens, montar Basic Auth, usar URL-safe e evitar erros comuns de padding e confusão com criptografia.
O módulo base64 resolve um problema cotidiano de backend e automação: como colocar bytes em um canal que só aceita texto. JSON, headers HTTP, e-mail MIME, variáveis de ambiente e muitos webhooks não transportam binário cru de forma confiável. Base64 transforma esses bytes em um alfabeto ASCII estável e, no caminho inverso, recupera o conteúdo original.
A recomendação direta é: use base64.b64encode() e base64.b64decode() para a maioria dos casos; use a variante urlsafe_* quando o valor for para URL; trabalhe sempre com bytes, não com str misturado; e nunca trate base64 como criptografia. Para tokens imprevisíveis, continue em secrets; para checksums e integridade, use hashlib; para IDs públicos, use uuid.
Neste guia, você vai codificar e decodificar strings e arquivos, embutir imagens em data URL, montar Basic Auth, entender padding, escolher entre base64 clássico e URL-safe e evitar os erros que mais quebram integrações no Brasil — de QR Code PIX a anexos de e-mail e payloads de API.
O que base64 resolve — e o que ele não resolve
Base64 mapeia grupos de 3 bytes (24 bits) para 4 caracteres de um alfabeto de 64 símbolos. O resultado cresce cerca de 33% em relação ao binário original, mas passa por sistemas que rejeitam bytes de controle, nulos ou acentos mal interpretados.
Isso é útil quando você precisa:
- embutir uma imagem pequena em JSON ou HTML (
data:image/png;base64,...); - transportar um certificado, chave pública ou PDF em uma API de texto;
- montar o header
Authorization: Basic ...; - representar um hash ou token em um formato legível;
- decodificar QR Codes, webhooks e respostas de PSP de pagamento.
Base64 não esconde o conteúdo. Qualquer pessoa com a string decodifica em milissegundos. Base64 não autentica origem. Base64 não substitui HTTPS, HMAC nem assinatura digital. Se a string vazou, o segredo vazou.
Codificar e decodificar texto em Python
O fluxo mais comum começa com texto Unicode convertido para bytes UTF-8:
import base64
texto = "Pedido #1042 — São Paulo"
dados = texto.encode("utf-8")
codificado = base64.b64encode(dados)
print(codificado)
# b'UGVkaWRvICMxMDQyIOKAkyBTw6NvIFBhdWxv'
# Para APIs e logs, a forma textual é o usual:
como_texto = codificado.decode("ascii")
print(como_texto)
No caminho inverso:
import base64
recebido = "UGVkaWRvICMxMDQyIOKAkyBTw6NvIFBhdWxv"
bytes_originais = base64.b64decode(recebido)
texto = bytes_originais.decode("utf-8")
print(texto)
# Pedido #1042 — São Paulo
Três regras evitam 90% dos bugs:
b64encoderecebe e devolvebytes.- Se a API pede string, faça
.decode("ascii")depois de codificar. - Ao decodificar, o resultado continua sendo
bytesaté você escolher o encoding do conteúdo (UTF-8, Latin-1, binário puro).
Se o payload for JSON, prefira serializar com json.dumps e só então codificar bytes — veja o guia de JSON em Python.
Arquivos, imagens e data URL
Para um arquivo binário, leia em modo rb e codifique o conteúdo inteiro ou em blocos se o tamanho for grande:
import base64
from pathlib import Path
caminho = Path("comprovante.png")
conteudo = caminho.read_bytes()
b64 = base64.b64encode(conteudo).decode("ascii")
data_url = f"data:image/png;base64,{b64}"
print(data_url[:80], "...")
Data URLs são comuns em HTML de e-mail, pré-visualização de upload e respostas de APIs de QR Code. No ecossistema brasileiro de pagamentos, vários PSPs devolvem o QR Code PIX já em base64 — o padrão aparece no guia de integração de pagamentos PIX com Python.
Para gravar de volta em disco:
import base64
from pathlib import Path
recebido = "iVBORw0KGgoAAAANSUhEUg..." # truncado no exemplo
Path("saida.png").write_bytes(base64.b64decode(recebido))
Use pathlib para caminhos e, se o arquivo for intermediário de um pipeline, considere tempfile para não deixar lixo no sistema.
Basic Auth e headers HTTP
O esquema Basic concatena usuario:senha, codifica em base64 e envia no header. Em produção, isso só faz sentido sobre HTTPS — e, mesmo assim, tokens de API ou OAuth costumam ser melhores.
import base64
usuario = "agencia"
senha = "segredo-temporario"
token = base64.b64encode(f"{usuario}:{senha}".encode("utf-8")).decode("ascii")
headers = {
"Authorization": f"Basic {token}",
"Accept": "application/json",
}
Com httpx ou requests, muitas vezes você não monta o header na mão:
import httpx
with httpx.Client(timeout=20.0) as client:
resposta = client.get(
"https://httpbin.org/basic-auth/agencia/segredo-temporario",
auth=("agencia", "segredo-temporario"),
)
print(resposta.status_code)
Para timeouts, retries e clientes modernos, veja httpx em Python e o guia de timeouts e retries com httpx.
URL-safe: quando + e / quebram o fluxo
O alfabeto clássico inclui + e /. Em query strings, esses caracteres precisam de percent-encoding e frequentemente chegam alterados. A variante URL-safe troca:
+→-/→_
import base64
payload = b'{"pedido":1042,"uf":"SP"}'
classico = base64.b64encode(payload).decode("ascii")
urlsafe = base64.urlsafe_b64encode(payload).decode("ascii")
print(classico)
print(urlsafe)
Na decodificação, use a função correspondente:
import base64
valor = "eyJwZWRpZG8iOjEwNDIsInVmIjoiU1AifQ=="
print(base64.urlsafe_b64decode(valor))
Para tokens de recuperação de senha e links de uso único, a escolha mais simples e segura costuma ser secrets.token_urlsafe(32), que já devolve texto URL-safe com aleatoriedade forte. Base64 entra quando você precisa representar bytes existentes (um ciphertext, um protobuf, um arquivo), não quando o objetivo é gerar um segredo.
Padding (=), whitespace e o erro Incorrect padding
Base64 exige que o comprimento da string seja múltiplo de 4. Quando faltam bytes no final, a codificação acrescenta um ou dois =. Algumas APIs e JWT omitem o padding; outras inserem quebras de linha a cada 76 caracteres (MIME).
import base64
import re
def decodificar_flexivel(valor: str) -> bytes:
limpo = re.sub(r"\s+", "", valor)
# completa padding se a origem omitiu os '='
resto = len(limpo) % 4
if resto:
limpo += "=" * (4 - resto)
return base64.b64decode(limpo, validate=False)
Boas práticas:
- remova espaços e quebras de linha antes de decodificar;
- se a origem garante padding e alfabeto corretos, use
validate=Truepara falhar cedo; - nunca decodifique cegamente um blob gigante vindo da internet sem limite de tamanho;
- trate
binascii.Errore responda com 400 em APIs, não com 500 genérico.
base64 e e-mail MIME
Anexos de e-mail historicamente viajam em base64. O ecossistema de email do Python já cuida disso em níveis mais altos, mas entender a camada ajuda a depurar:
import base64
from email.message import EmailMessage
msg = EmailMessage()
msg["Subject"] = "Relatório mensal"
msg["From"] = "[email protected]"
msg["To"] = "[email protected]"
msg.set_content("Segue o PDF em anexo.")
pdf = open("relatorio.pdf", "rb").read()
msg.add_attachment(
pdf,
maintype="application",
subtype="pdf",
filename="relatorio.pdf",
)
Por baixo, o anexo é transferido com content-transfer-encoding base64. Para automação completa de SMTP, templates e anexos, veja automação de e-mails com Python.
Tabela rápida de decisões
| Situação | Função / abordagem |
|---|---|
| Bytes → texto em JSON/API | base64.b64encode(dados).decode("ascii") |
| Texto base64 → bytes | base64.b64decode(texto) |
| Valor em URL ou path | urlsafe_b64encode / urlsafe_b64decode |
| Token aleatório seguro | secrets.token_urlsafe (não invente com random) |
| Integridade de arquivo | hashlib.sha256 + comparação segura |
| Identificador público | uuid4 / uuid7 |
| Esconder segredo | HTTPS + criptografia / cofre — não base64 |
Exemplo prático: webhook que recebe arquivo em base64
Um padrão comum em ERPs, CRMs e fintechs brasileiras é receber o documento já codificado no JSON:
import base64
import json
from pathlib import Path
def salvar_documento_do_webhook(corpo: str, destino: Path, limite_bytes: int = 5_000_000) -> Path:
payload = json.loads(corpo)
nome = Path(payload["filename"]).name # evita path traversal
bruto = base64.b64decode(payload["content_base64"], validate=True)
if len(bruto) > limite_bytes:
raise ValueError("arquivo excede o limite permitido")
caminho = destino / nome
caminho.write_bytes(bruto)
return caminho
Pontos de segurança do exemplo:
Path(...).namedescarta diretórios maliciosos no nome;validate=Truerejeita alfabeto inválido cedo;- limite de tamanho evita esgotar disco e memória;
- o conteúdo continua não confiável depois de salvo — não execute o arquivo e não confie só na extensão.
Para orquestrar ferramentas externas depois do download, combine com subprocess e, se for montar linha de comando, com shlex.
Erros comuns (e como evitar)
1. Confundir codificação com criptografia.
Colocar uma senha em base64 no banco é o mesmo que salvá-la quase em texto puro. Use hash de senha adequado e, para tokens, secrets.
2. Codificar a string sem .encode().b64encode não aceita str. Defina o encoding explicitamente (utf-8 na maioria dos casos brasileiros com acentos).
3. Misturar variantes clássica e URL-safe.
Se a origem usou urlsafe_b64encode, decodifique com urlsafe_b64decode. Trocar as funções gera padding/alfabeto inválido.
4. Logar o payload completo.
Data URLs e PDFs em base64 estouram logs e podem vazar PII. Registre tamanho, hash curto e correlacionador — não o blob inteiro.
5. Assumir que “parece texto” é texto.
Depois de b64decode, você tem bytes. Só chame .decode("utf-8") se o contrato da API garantir Unicode; caso contrário, trate como binário.
Quando base64 não é a melhor ferramenta
- Arquivos grandes entre serviços: prefira upload direto para S3/R2/MinIO e troque só a URL — veja upload de arquivos com Python.
- Streaming de mídia: base64 em JSON aumenta payload e memória; use multipart ou object storage.
- Segredos de aplicação: variáveis de ambiente e cofres (não base64 de senha hardcoded).
- Comparação de conteúdo: use hash (
hashlib), não a string base64, para fingerprints estáveis e curtos.
Checklist de produção
- Defina se o canal exige texto (JSON, header, env) ou se aceita binário/multipart.
- Escolha clássico vs URL-safe de propósito, e documente no contrato da API.
- Trate padding e whitespace de forma explícita na borda do sistema.
- Limite tamanho antes e depois de decodificar.
- Nunca use base64 como controle de acesso ou confidencialidade.
- Combine com HTTPS, autenticação real e, quando fizer sentido, HMAC ou assinatura.
- Cubra o fluxo com testes de ida e volta (
encode → decode == original) e de entrada inválida.
Perguntas frequentes
Para que serve o módulo base64 em Python?
Para representar dados binários como texto ASCII portátil. Ele aparece em anexos, data URLs, Basic Auth, webhooks e respostas de pagamento. Não protege o conteúdo contra leitura.
base64 é a mesma coisa que criptografia?
Não. Qualquer um decodifica base64 sem chave. Criptografia, tokens e hashes resolvem problemas diferentes e devem ser combinados com consciência.
Qual a diferença entre b64encode e urlsafe_b64encode?
A variante URL-safe evita + e /, reduzindo atrito em query strings e paths. Use a mesma família na codificação e na decodificação.
Por que aparece Incorrect padding?
String truncada, whitespace residual ou padding = omitido. Normalize a entrada e complete o comprimento para múltiplo de 4 quando o contrato permitir.
Preciso instalar alguma biblioteca?
Não. import base64 basta. O restante do stack (HTTP, e-mail, storage, validação) é que pode puxar dependências externas.
Próximos passos
Se você está montando um backend ou automação em Python no Brasil, encaixe base64 no lugar certo da cadeia:
- gere segredos com
secrets; - verifique integridade com
hashlib; - identifique recursos com
uuid; - troque JSON com o guia de JSON;
- envie documentos por e-mail ou object storage.
Base64 é uma ferramenta de transporte e representação. Usada assim, ela some do caminho e deixa o sistema falar bytes com canais que só entendem texto — sem a falsa sensação de segurança que tanto atrapalha código de produção.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português