Base64 não é criptografia — o que é, quando usar e a variante URL-safe
Como o Base64 transforma bytes em texto, por que ele não esconde nada, como tratar acentos e emoji do jeito certo e quando usar o alfabeto URL-safe.
Por Felipe YamaokaPublicado em 5 de novembro de 20265 min de leitura
A senha "criptografada" na configuração
De vez em quando alguém encontra um arquivo de configuração com uma linha como password: c2VjcmV0MTIz e acha que ela está protegida. Não está. Cole num decodificador de Base64 qualquer e volta secret123, na hora, sem chave nenhuma.
O Base64 está em todo lugar: na autenticação HTTP Basic, nos JWTs, nas data URLs, nos anexos de e-mail e nas APIs que precisam mandar um arquivo dentro de um JSON, como o XML de uma nota fiscal ou o PDF de um boleto. Ele é útil justamente por ser simples e reversível. Este guia explica o que ele faz, o que ele não faz e os dois detalhes que causam a maioria dos bugs: a codificação do texto e o alfabeto URL-safe.
O que o Base64 faz
Muitos canais foram feitos para texto, e não para bytes quaisquer. Strings JSON, headers HTTP, URLs e sistemas de e-mail antigos podem quebrar ou alterar dados binários. O Base64 resolve isso representando qualquer sequência de bytes com só 64 caracteres seguros: A a Z, a a z, 0 a 9, + e /, além do = para preenchimento.
O mecanismo cabe numa frase: pegue a entrada de 3 em 3 bytes (24 bits), divida em 4 grupos de 6 bits e escreva cada grupo como um dos 64 caracteres. Quando o tamanho da entrada não é múltiplo de 3, a saída ganha um ou dois = no fim para continuar múltipla de 4.
Daí saem duas consequências:
- A saída fica cerca de 33% maior que a entrada, já que cada 3 bytes viram 4 caracteres. 300 bytes de dados viram 400 caracteres.
- Qualquer pessoa consegue desfazer. Não existe chave, segredo nem aleatoriedade. A mesma entrada sempre gera a mesma saída.
Codificar não é criptografar
Este é o ponto que vale repetir. Base64 é uma codificação, como escrever um número em hexadecimal. Ele muda a representação, e não o sigilo.
- Codificação (Base64, hexadecimal, URL encoding) faz o dado caber num canal. Qualquer um desfaz.
- Criptografia (AES, ChaCha20, RSA) deixa o dado ilegível sem a chave.
- Hash (SHA-256, bcrypt, Argon2) transforma o dado numa impressão digital que não pode ser desfeita, e é o que se usa para senhas.
A autenticação HTTP Basic mostra bem por que a diferença importa. O header Authorization: Basic dXNlcjpwYXNz é só user:pass em Base64. Ele é seguro apenas porque a conexão usa HTTPS; o Base64 não acrescenta proteção nenhuma. O mesmo vale para o payload de um JWT, que qualquer um decodifica, como mostra o guia de como ler um JWT.
Se algo precisa ficar em segredo, criptografe ou guarde num gerenciador de segredos. Se você precisa guardar uma senha, use um hash lento feito para isso. Nunca confie no Base64 para esconder dados, e lembre que, pela LGPD, dado pessoal "escondido" em Base64 continua sendo dado pessoal exposto.
Texto, bytes e UTF-8
O Base64 trabalha com bytes, e não com caracteres. Antes de codificar um texto, ele precisa virar bytes, e é nesse passo que acentos e emoji dão errado.
No navegador, as funções antigas btoa e atob só lidam com caracteres da faixa Latin-1. Veja o que acontece:
btoa("olá, mundo"); // "b2zhLCBtdW5kbw==" bytes Latin-1, não UTF-8
btoa("café ☕"); // lança InvalidCharacterErrorO primeiro resultado é Base64 válido, mas usa um byte por letra acentuada em vez da codificação UTF-8 que quase todo outro sistema espera, então decodificar em outra linguagem gera caracteres estranhos. O segundo simplesmente falha. O jeito certo é converter o texto para bytes UTF-8 antes:
function toBase64(text) {
const bytes = new TextEncoder().encode(text);
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
function fromBase64(base64) {
const binary = atob(base64);
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
return new TextDecoder().decode(bytes);
}
toBase64("café ☕"); // "Y2Fmw6kg4piV"No Node.js e no Bun, o Buffer faz isso por você: Buffer.from("café ☕").toString("base64"). No Python, codifique a string antes: base64.b64encode("olá, mundo".encode()) dá b2zDoSwgbXVuZG8=.
O Codificador/Decodificador Base64 deste site sempre usa UTF-8 nos dois sentidos, então acentos e emoji vão e voltam sem estragar. Quando os bytes decodificados não são UTF-8 válido, por exemplo porque o Base64 guarda uma imagem ou um arquivo binário, ele avisa em vez de mostrar caracteres quebrados.
A variante URL-safe
O Base64 padrão usa +, / e =, e os três têm significado especial em URLs e nomes de arquivo. O + pode virar espaço numa query string, a / parece separador de caminho e o = separa chave de valor.
O alfabeto URL-safe, o Base64URL definido na RFC 4648, resolve isso:
+vira-/vira_- o preenchimento
=normalmente é removido
Por exemplo, o texto Hi?> é SGk/Pg== no Base64 padrão e SGk_Pg no Base64URL. JWTs, muitos tokens de API e URLs assinadas usam a forma URL-safe.
Dois cuidados práticos. Primeiro, nem toda biblioteca remove o preenchimento: o base64.urlsafe_b64encode(b"Hi?>") do Python devolve SGk_Pg==, e alguns decodificadores recusam a entrada sem =, então pode ser preciso acrescentar de volta (até o tamanho ser múltiplo de 4). Segundo, não decodifique entrada URL-safe com um decodificador padrão, senão o - e o _ são recusados como caracteres inválidos.
Quando usar Base64
Bons usos:
- Dados binários dentro de formatos de texto, como um arquivo pequeno ou uma assinatura dentro de um JSON.
- Data URLs para imagens ou fontes pequenas em CSS e HTML:
data:image/png;base64,.... - Protocolos que exigem, como autenticação Basic, anexos de e-mail (MIME) e JWT.
- Identificadores opacos em URLs, com o alfabeto URL-safe.
Maus usos:
- Esconder segredos. Qualquer um decodifica.
- Arquivos grandes em APIs. Os 33% a mais e a memória gasta para codificar e decodificar pesam. Envie o arquivo direto, ou use multipart, e mande um link.
- Compressão. O Base64 sempre aumenta o tamanho.
Erros comuns
- Tratar Base64 como proteção para senhas, tokens ou dados pessoais.
- Usar
btoadireto em texto com acentos ou emoji. - Misturar o alfabeto padrão com o URL-safe.
- Esquecer que alguns decodificadores precisam do preenchimento
=. - Colocar quebras de linha dentro da string codificada, o que algumas ferramentas de MIME fazem a cada 76 caracteres e muitos decodificadores recusam.
- Codificar o mesmo dado duas vezes sem querer, o que fica fácil de notar quando o resultado decodifica para outra string em Base64.
Resumo
O Base64 transforma bytes em 64 caracteres seguros para que dados binários passem por canais de texto. Ele aumenta o dado em cerca de um terço e qualquer pessoa desfaz, então nunca é medida de segurança. Converta o texto para bytes UTF-8 antes de codificar, use o alfabeto URL-safe em URLs e tokens e recorra a criptografia ou hash de verdade sempre que algo precisar ficar em segredo.
Leia também
A regex que trava o navegador — backtracking catastrófico explicado
Por que uma expressão regular curta pode levar segundos ou travar a página, como reconhecer quantificadores aninhados e como reescrever o padrão.
5 de novembro de 20266 min de leituraFerramenta: Testador de Regex
De uma coluna da planilha para um array em JavaScript, Python ou SQL IN
Como transformar uma coluna colada numa lista JavaScript ou Python, numa cláusula SQL IN ou em CSV, tratando aspas, duplicados, linhas vazias e listas grandes.
5 de novembro de 20265 min de leituraFerramenta: Coluna para Array