Origens: o correio só sabia carregar texto
O existe porque os primeiros sistemas de correio foram especificados para texto e nada mais. Uma mensagem era de caracteres de sete bits, alguns valores de byte significavam controle e não conteúdo, e gateways no caminho se sentiam livres para reescrever fins de linha e derrubar o oitavo bit. Qualquer coisa binária enviada por ali só sobrevivia por acidente.
A resposta foi parar de enviar binário. Pegue três bytes, trate-os como vinte e quatro bits, divida em quatro grupos de seis, e mapeie cada grupo num de sessenta e quatro caracteres escolhidos para serem seguros em todo lugar. A saída é maior e completamente desinteressante para qualquer gateway, que é justamente o ponto.
A padronização veio com o , no início dos anos 1990, e vale notar o que o MIME de fato fez: ele não tornou o correio capaz de binário, tornou o texto capaz de carregar qualquer coisa. Todo anexo já enviado é essa decisão, ainda sendo paga em banda.
Por que embrulhar binário em texto
Muitos meios de transporte foram feitos para texto, não para bytes brutos: corpos de e-mail, documentos JSON e XML, cabeçalhos HTTP, arquivos-fonte. Coloque binário bruto em qualquer um deles e um byte de controle ou nulo perdido pode quebrar a análise. O Base64 resolve isso reexpressando os bytes usando apenas caracteres imprimíveis seguros, de modo que o binário possa trafegar dentro de um canal somente texto intacto. Eis onde isso realmente acontece.
Os cenários comuns
- Data URIs. Uma URL
data:incorpora um pequeno recurso diretamente na marcação ou no CSS, por exemplodata:image/png;base64,iVBORw0KGgo.... Os bytes da imagem ficam em Base64 ali mesmo no atributo, poupando uma requisição de rede separada para recursos minúsculos. - E-mail (MIME). Anexos e corpos não- são transportados com
Content-Transfer-Encoding: base64. O MIME quebra a saída em linhas (classicamente de 76 caracteres) para que servidores de correio antigos não engasguem com linhas longas. - . Certificados e chaves são bytes embrulhados em Base64 entre os marcadores
-----BEGIN CERTIFICATE-----e-----END CERTIFICATE-----, com linhas quebradas em 64 caracteres. O PEM é o motivo pelo qual um certificado é um bloco de texto que você pode colar em um arquivo de configuração. - Autenticação HTTP Basic. O cabeçalho
Authorization: Basicé o Base64 deusuário:senha.
O custo de tamanho
O Base64 não é de graça. Três bytes viram quatro caracteres, então a forma codificada é cerca de 33% maior que a original, antes de qualquer sobrecarga de quebra de linha. Essa troca geralmente vale a pena pela correção em um canal de texto, mas é o motivo pelo qual você não codificaria em Base64 um arquivo grande que poderia enviar como bytes brutos. Para pequenos recursos embutidos, a conveniência vence; para cargas grandes, a sobrecarga favorece um transporte binário.
Onde ele aparece, e por que continua se espalhando
O padrão se repete toda vez que um canal em formato de texto precisa carregar bytes:
- Anexos de e-mail, o caso original, ainda codificados do mesmo jeito.
- Autenticação básica do HTTP, em que a credencial é Base64 - codificada, e não protegida, que é a leitura errada mais comum da área.
- Tokens e certificados. O PEM é Base64 com linhas de cabeçalho; um JSON Web Token são três segmentos Base64url unidos por pontos, e a carga é legível por quem tiver o token.
- URLs de dados e imagens embutidas, trocando tamanho por uma requisição a menos.
- Configuração e segredos em variáveis de ambiente, em que o valor precisa ser uma linha só de texto.
- Malware e phishing, pela mesma razão de todo mundo: move bytes por algo que só aceita texto.
A variante segura para URL, e por que ela existe
O Base64 padrão usa + e /, e os dois significam outra coisa numa URL - um espaço na string de consulta, e um separador de caminho. O Base64url os troca por - e _, e em geral descarta o preenchimento final. Não é outra codificação, e misturar as duas é defeito recorrente: um token que decodifica numa biblioteca e falha noutra costuma ser isso.
O que ele não é
Não é cifragem. Não há chave. Qualquer coisa em Base64 é legível por quem reparar, e chamar um valor codificado de ofuscado é o erro da obscuridade em miniatura: nada continua verdadeiro depois que a codificação sai.
Não é integridade. O Base64 codifica alegremente uma carga modificada, e a decodifica de volta.
Não é sinal de má-fé. Vê-lo num payload diz que um canal precisava de texto, e nada sobre o conteúdo. Analista que trata conteúdo codificado como suspeito em si acaba perseguindo todo JSON Web Token do parque.
A rotina prática
- Decodifique, sempre - a pergunta é o que são os bytes, e a codificação não responde nada.
- Confira a variante antes de culpar o decodificador:
-e_significam Base64url. - Fique atento a codificações empilhadas, que costumam ser deliberadas.
- Dimensione: cerca de um terço maior, o que importa para token carregado em toda requisição e para qualquer coisa armazenada por linha.
- Se o valor for segredo, lembre que a codificação não fez nada por ele. Trate uma credencial em Base64 exatamente como trataria a original.
Codificação não é criptografia
O exemplo do Basic auth já deixa o ponto mais importante claro. Authorization: Basic dXNlcjpwYXNz parece opaco, mas dXNlcjpwYXNz é simplesmente o Base64 de user:pass, reversível por qualquer pessoa, sem nenhuma chave envolvida. O Base64 não oferece nenhuma confidencialidade. É uma codificação (uma mudança reversível de representação), não criptografia (um segredo protegido por chave) e nem hashing (uma impressão digital de mão única). É por isso que o Basic auth só é seguro sobre TLS: o transporte criptografa o cabeçalho, porque o Base64 certamente não o faz.
Sempre que você vir uma string longa de aparência opaca, decodificá-la primeiro é uma forma barata de descobrir o que ela é. A ferramenta Base64 decodifica data URIs, corpos PEM e cabeçalhos de autenticação para seus bytes e texto brutos, inteiramente no seu navegador, então nada do que você cola sai da página.
Codificações empilhadas: dupla, tripla e além
Como a codificação é reversível e não carrega nenhuma chave, é trivial aplicá-la mais de uma vez. Faça o Base64 da saída de um Base64 e você tem uma string duplamente codificada; repita para obter uma tripla. Cada passagem é legítima por si só, e o resultado ainda é apenas texto, que é exatamente por que é um recurso de ofuscação predileto. Atacantes empilham codificações (às vezes misturando Base64, URL/percent e hexadecimal entre as camadas) para que uma carga passe por um filtro ou uma assinatura que só inspeciona a forma mais externa, ou a primeira decodificada. Uma regra de (firewall de aplicação web, do inglês web application firewall) que casa com <script nunca a vê se a requisição carrega PHNjcmlwdA== embrulhado mais uma vez.
A defesa é simétrica: para entender um blob suspeito, você descasca as camadas, decodificando repetidamente até chegar a um texto plausível em vez de mais uma string codificada. O sinal de que você ainda não terminou é que uma decodificação produz algo que continua parecendo codificado (todo o alfabeto Base64, ou cheio de escapes %). Um hábito prático é continuar decodificando até a saída parar de mudar de forma.
A ferramenta Base64 apoia isso diretamente: o controle de camadas (×1, ×2, ×3) codifica uma carga em várias passagens, ou remove várias ao decodificar. Se você pedir para remover mais camadas do que uma carga realmente tem, ela para na primeira camada que falha ao decodificar e mostra exatamente onde, para você ver quão profundo o aninhamento realmente foi. É a mesma razão pela qual ferramentas de segurança que normalizam a entrada decodificam recursivamente antes de casar: uma regra que inspeciona apenas uma camada é uma regra que um base64 a mais derrota.