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.

9 min de leitura Equipe Python Brasil

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:

  1. b64encode recebe e devolve bytes.
  2. Se a API pede string, faça .decode("ascii") depois de codificar.
  3. Ao decodificar, o resultado continua sendo bytes até 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=True para falhar cedo;
  • nunca decodifique cegamente um blob gigante vindo da internet sem limite de tamanho;
  • trate binascii.Error e 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çãoFunção / abordagem
Bytes → texto em JSON/APIbase64.b64encode(dados).decode("ascii")
Texto base64 → bytesbase64.b64decode(texto)
Valor em URL ou pathurlsafe_b64encode / urlsafe_b64decode
Token aleatório segurosecrets.token_urlsafe (não invente com random)
Integridade de arquivohashlib.sha256 + comparação segura
Identificador públicouuid4 / uuid7
Esconder segredoHTTPS + 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(...).name descarta diretórios maliciosos no nome;
  • validate=True rejeita 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

  1. Defina se o canal exige texto (JSON, header, env) ou se aceita binário/multipart.
  2. Escolha clássico vs URL-safe de propósito, e documente no contrato da API.
  3. Trate padding e whitespace de forma explícita na borda do sistema.
  4. Limite tamanho antes e depois de decodificar.
  5. Nunca use base64 como controle de acesso ou confidencialidade.
  6. Combine com HTTPS, autenticação real e, quando fizer sentido, HMAC ou assinatura.
  7. 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:

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.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português