Codificação Base64 em Perl: um guia completo
Você tem dados que precisam sobreviver a um canal que não gosta deles. Um blob binário que precisa morar num campo JSON. Uma imagem que tem que viver dentro de uma tag HTML. Um certificado que pertence a um arquivo de configuração. Um token que vai viajar por URLs, cabeçalhos e strings de query. Essa é a vida diária da codificação Base64: ela reescreve cada três bytes de dados crus como quatro caracteres de um alfabeto de 64 letras, com um ou dois sinais de = finalizando o rabo, para que o resultado seja texto simples que qualquer coisa pode carregar, tipicamente ficando uns 33 por cento mais longo do que a origem. A página inicial deste site explica o formato em profundidade, então este artigo foca no que o Perl te dá, no que ele decide em silêncio por você e em onde estão as armadilhas.
A boa notícia primeiro: o encode_base64 mora no core do Perl desde 2002, é implementado em C e é confortavelmente rápido. A parte interessante é que a função tem opiniões. Ela quebra a saída em 76 caracteres, anexa uma quebra de linha no final e não codifica caracteres Unicode que você não converteu em bytes antes: acima da faixa Latin-1 ela morre na hora, e abaixo dessa linha ela assume em silêncio bytes Latin-1. Este guia percorre cada sabor de Base64 que um desenvolvedor Perl realmente produz: o one-liner, o corpo de e-mail quebrado em MIME, a chave quebrada em PEM, o token URL-safe e a versão em streaming para arquivos grandes demais para caber em memória.
Uma função, uma quebra de linha escondida
A API inteira, exatamente como a documentação moderna apresenta:
encode_base64( $bytes )
encode_base64( $bytes, $eol )
Leia de novo. Dois argumentos, um deles opcional, um valor de retorno, sem flags. O segundo argumento opcional é a sequência de fim de linha, e o padrão é uma quebra de linha simples, o que significa que a chamada mais inocente do Perl produz saída com quebra de linha e terminada em newline:
use MIME::Base64 qw(encode_base64);
my $wrapped = encode_base64("Aladdin:open sesame");
print length($wrapped), "\n"; # 29: os 28 caracteres mais um newline
my $single = encode_base64("Aladdin:open sesame", "");
print length($single), "\n"; # 28: passe uma string vazia para não quebrar linhas
O tamanho da saída segue um padrão fixo que você pode prever antes de chamar:
| Bytes de entrada | Caracteres de saída | Padding |
|---|---|---|
| 0 | 0 | nenhum |
| 1 | 4 | dois = |
| 2 | 4 | um = |
| 3 | 4 | nenhum |
| 100,000 | 133,336 | nenhum, em uma linha só |
| 3,000,000 | 4,000,000 | nenhum |
O padrão é quatro caracteres para cada grupo completo de três bytes, mais um grupo final incompleto completado com um ou dois sinais de =. Uma consequência que vale conhecer: um byte e três bytes produzem, nos dois casos, quatro caracteres, então o tamanho codificado esconde o tamanho exato da entrada. E se você precisa do tamanho sem fazer o trabalho, o módulo tem uma função de tamanho desde o 3.10 em 2010. Ela só não é exportada por padrão, então você chama ela pelo nome do pacote:
use MIME::Base64 ();
my $with_wrap = MIME::Base64::encoded_base64_length($bytes); # linhas de 76 caracteres, eol padrão
my $single_line = MIME::Base64::encoded_base64_length($bytes, ""); # sem quebra de linha
my $mime_body = MIME::Base64::encoded_base64_length($bytes, "\r\n");
Há mais uma regra para memorizar, porque é o único jeito que o codificador grita: se a string que você entrega contém caracteres com código acima de 255, o encode_base64 morre com Wide character in subroutine entry. Abaixo dessa linha a falha é mais quieta: caracteres até 255 são rebaixados em silêncio para os bytes Latin-1 deles, então uma string de caracteres acentuados que pulou a conversão é codificada como Latin-1 em vez de UTF-8, e ninguém te avisa. A codificação Base64 só está definida para caracteres de um byte, e o Perl 5.8 e superiores permitem caracteres estendidos em strings, então a conversão é uma decisão que você toma de propósito, com o Encode, na próxima seção.
Texto ou bytes? A etapa que a função não faz
Strings do Perl carregam uma flag discreta que diz se elas seguram caracteres ou bytes, e o Base64 mora do lado dos bytes nessa linha. Se o seu texto é uma string de caracteres, e é o momento em que ele vem de um parser JSON, de um template ou de um literal com letras acentuadas num arquivo de fonte UTF-8, o codificador recusa adivinhar que bytes você quis para caracteres acima da faixa Latin-1, e te avisa; abaixo da faixa ele adivinha Latin-1 em silêncio. A correção é a mesma nos dois casos, e é uma função core do módulo Encode:
use MIME::Base64 qw(encode_base64);
use Encode qw(encode);
my $chars = "H\x{eb}llo W\x{f6}rld"; # uma string de caracteres
my $utf8 = encode("UTF-8", $chars); # agora: bytes
my $b64 = encode_base64($utf8, "");
print $b64, "\n"; # SMOrbGxvIFfDtnJsZA==
Aquela chamada de encode é a dança inteira: escolha a representação em bytes, e UTF-8 para qualquer coisa moderna, converta os caracteres para aqueles bytes, e só então entregue os bytes ao codificador. Para texto ocidental legado que chegou como Windows 1252, a conversão é a mesma função com um nome diferente, encode("Windows-1252", $legacy), que te entrega a forma original de um byte. O módulo Encode é core, então tudo isso não custa nada.
Agora a armadilha. Se os bytes que você tem já são UTF-8 e você passa eles por um encode("UTF-8", ...) de novo, pensando que está deixando eles UTF-8, você não ganha uma cópia: você ganha uma codificação dupla, onde cada caractere acentuado incha para virar dois caracteres por conta própria. O sintoma clássico é texto que antes lia Hëllo e agora lê Hëllo, e qualquer decodificador da internet vai decodificar aquilo com fidelidade para você:
use Encode qw(encode decode);
my $right = encode("UTF-8", "H\x{eb}llo");
my $wrong = encode("UTF-8", $right); # re-codificando os bytes como caracteres
print decode("UTF-8", $right), "\n"; # Hëllo
print decode("UTF-8", $wrong), "\n"; # Hëllo
A regra prática que previne: bytes são codificados exatamente uma vez, e o utf8::is_utf8() te mostra de qual lado da linha uma string está. Se a flag está definida, você segura caracteres e a chamada encode() é a jogada certa; se não está definida, você segura bytes e está pronto para o Base64.
Quebra de linha: três dialetos, uma regra
Como a saída padrão é quebrada e terminada em newline, a primeira decisão de todo trabalho de codificação é um problema de destino: onde essa string vai morar? O fato unificador é que decodificadores ignoram quebras de linha por completo: a RFC 2045 manda o software de decodificação ignorar todas as quebras de linha e os caracteres fora do alfabeto, então a quebra de linha é uma cortesia para ferramentas baseadas em linhas e humanos, não uma diferença semântica. As três respostas na prática:
Sem quebras de linha. A saída da função com a quebra de linha desligada, exatamente uma linha. É isso que você quer para URLs, payloads JSON, cabeçalhos, valores de banco de dados e qualquer outra coisa onde uma quebra de linha seria um bug. É também o que a maioria das pessoas quer dizer quando pede só o Base64:
my $single = encode_base64($bytes, "");
MIME: 76 caracteres mais CRLF. A convenção de e-mail da RFC 2045: linhas codificadas não podem passar de 76 caracteres, e o mundo MIME fala CRLF. Essa é função do próprio módulo, feita com o segundo argumento:
my $mime_body = encode_base64($bytes, "\r\n");
PEM: 64 caracteres mais LF. Chaves e certificados usam a convenção mais antiga com linhas menores de 64 caracteres, e o módulo não produz essa largura sozinho, então um helper de quatro linhas preenche a lacuna:
sub wrap_lines {
my ($text, $width) = @_;
return join "\n", $text =~ /(.{1,$width})/g;
}
my $pem = "-----BEGIN CERTIFICATE-----\n"
. wrap_lines(encode_base64($der, ""), 64) . "\n"
. "-----END CERTIFICATE-----\n";
O mesmo helper serve para os outros dialetos de 64 caracteres - a codificação textual PKIX da RFC 7468 e a armadura OpenPGP, cujas linhas de dados têm 64 caracteres de largura e cuja linha de checksum CRC24 no final o GnuPG adiciona, não você. Uma pegadinha se aplica a todos: o encode_base64 anexa o fim de linha no final exato do resultado, mesmo quando a última linha enche a largura exata. Se um consumidor a jusante tropeçar nessa linha em branco no final, uma chamada de rtrim no resultado resolve.
Base64 URL-safe: o alfabeto do - e do _
O alfabeto padrão inclui o + e o /, e os dois são problema fora de um arquivo de texto: um + numa string de query codificada em formulário vira um espaço antes da sua aplicação ver, e o / é um separador de caminho em URLs. Nomes de arquivo e tokens têm as próprias reclamações. A RFC 4648, seção 5, resolve isso com o alfabeto seguro para URL e nome de arquivo, onde o + vira -, o / vira _ e o padding de = no final geralmente é descartado. A RFC insiste que isso não deve ser considerado a mesma coisa que a codificação base64, então trate como um formato distinto, comumente chamado de base64url. O Perl produz em uma única chamada desde a versão 3.11 em 2010:
use MIME::Base64 qw(encode_base64url);
my $seg = encode_base64url("sunset-42");
print $seg, "\n"; # c3Vuc2V0LTQy: sem padding, sem newline
Essa chamada única faz as três mudanças: a troca de alfabeto, sem padding, sem quebras de linha. Se você já segura Base64 padrão e o destino quer o dialeto URL-safe, duas operações de string convertem no lugar:
sub to_urlsafe {
my ($b64) = @_;
$b64 =~ tr{+/}{-_};
return $b64 =~ s/=+\z//r;
}
my $seg = to_urlsafe(encode_base64($bytes, ""));
Quando usar: partes de JSON Web Token, parâmetros state e nonce do OAuth, IDs de API que você coloca em caminhos de URL, e chaves opacas que precisam sobreviver a uma barra de endereço ou a um nome de arquivo, onde o Data::UUID::Base64URLSafe do CPAN existe exatamente para isso. Quando não usar: corpos de e-mail, armadura PEM e qualquer lugar onde um consumidor de alfabeto padrão está do outro lado, porque - e _ não estão no vocabulário deles. E não misture os dois alfabetos em silêncio: um valor codificado URL-safe deve ser decodificado URL-safe, em todo lugar, para sempre. Em Perls anteriores ao 3.11, o módulo standalone MIME::Base64::URLSafe de 2006, que é um port do codec urlsafe do Python, fornece o urlsafe_b64encode; em qualquer coisa moderna, a função embutida é a ferramenta certa.
Montando um JWT: cada parte na mão
JSON Web Tokens são o consumidor principal do Base64 em APIs modernas, e eles usam o dialeto URL-safe, sem padding, da seção acima. Segundo a RFC 7515, um JWT compacto é três partes base64url separadas por pontos: o cabeçalho protegido, o payload e a assinatura. Montar um na mão é um jeito agradável de ver cada peça móvel:
use MIME::Base64 qw(encode_base64url);
use JSON::PP qw(encode_json);
use Digest::SHA qw(hmac_sha256);
my $secret = "correct-horse-battery-staple";
my $head = encode_base64url(encode_json({ alg => "HS256", typ => "JWT" }));
my $claims = encode_base64url(encode_json({ sub => "homer", role => "admin" }));
my $sig = encode_base64url(hmac_sha256("$head.$claims", $secret));
my $jwt = "$head.$claims.$sig";
print $jwt, "\n"; # um token HS256 compacto; a ordem das chaves dentro de cada parte JSON varia de execução em execução
Três detalhes que valem notar. Primeiro, o encode_json do módulo JSON::PP do core emite bytes UTF-8 compactos sem espaços, que é exatamente o que as specs JOSE querem dentro de um token. Segundo, o payload é legível por qualquer um, e é por design: um JWT é um ticket assinado, não um segredo, então nunca coloque valores confidenciais nos claims. Terceiro, a assinatura é a codificação base64url de bytes HMAC crus, e é por isso que o hmac_sha256 vai direto para o codificador sem qualquer formatação hexadecimal.
Em produção, você não faz assinatura na mão. O módulo do CPAN Crypt::JWT, que se constrói sobre o CryptX, implementa JWS e JWE com o conjunto completo de algoritmos:
use Crypt::JWT qw(encode_jwt);
my $jwt = encode_jwt(
payload => { sub => "homer", role => "admin" },
alg => "HS256",
key => $secret,
);
E no lado receptor, fixe o algoritmo com accepted_alg para que um atacante não consiga virar o token para uma variante mais fraca: decode_jwt(token => $jwt, key => $secret, accepted_alg => "HS256") verifica a assinatura e croaks em caso de falha. Fazer na mão é bom para entender; uma biblioteca é boa para dinheiro.
HTTP: cabeçalhos de autenticação, data URIs e o handshake WebSocket
O cabeçalho Authorization: Basic é o caso de uso vivo mais antigo: o nome de usuário e a senha unidos por dois-pontos, codificados em uma linha só, prefixados com a palavra do esquema. O segundo argumento de string vazia é portador de carga aqui, porque um newline no final dentro de um campo de cabeçalho é um bug:
use MIME::Base64 qw(encode_base64);
my $user = "alice";
my $pass = "s3cr3t";
my $header = "Basic " . encode_base64("$user:$pass", "");
print $header, "\n"; # Basic YWxpY2U6czNjcjN0
Data URIs da RFC 2397 são a mesma ideia aplicada a imagens: o payload mora direto na URL, então não precisa de um segundo pedido para buscá-lo. Mídias binárias usam a flag ;base64, então o payload é exatamente o que o encode_base64 produziu com a quebra de linha desligada:
my $uri = "data:" . $mime_type . ";base64," . encode_base64($bytes, "");
Os compromissos são reais, porém. O payload codificado fica uns 33 por cento maior que o arquivo, o que deixa o próprio documento HTML maior. Navegadores não fazem cache de uma data URI do jeito que fazem com a URL de um arquivo - não existe um fetch separado para fazer cache, então cada visualização de página despacha os bytes de novo como parte do documento, e a própria RFC diz que data URIs só são úteis para valores curtos. Use para avatares, ícones e gráficos inline pequenos; use arquivos de verdade para tudo o resto. Há um terceiro canto do HTTP que usa Base64 em silêncio: o handshake WebSocket da RFC 6455, onde o cliente envia um cabeçalho Sec-WebSocket-Key que é o Base64 de dezesseis bytes aleatórios. Frameworks como o Mojolicious fazem isso por você, mas se você um dia vir isso no fio, agora você sabe o que é:
use MIME::Base64 qw(encode_base64);
my $key = encode_base64(pack("C16", map { int(rand 256) } 1 .. 16), "");
print length($key), "\n"; # 24: dezesseis bytes aleatórios, completados até o grupo de quatro caracteres
Arquivos: slurps, chunks de 57 bytes e a CLI
O trabalho de codificação mais direto: um arquivo vira texto. Strings do Perl são bytes, então não existe modo binário para procurar - a camada :raw é a coisa inteira. Abra raw, leia, codifique, escreva:
use MIME::Base64 qw(encode_base64);
open my $in, "<:raw", $ARGV[0] or die $!;
local $/;
my $bytes = <$in>;
close $in;
open my $out, ">:raw", $ARGV[1] or die $!;
print {$out} encode_base64($bytes);
close $out;
As camadas :raw importam. Sem elas, o Perl tentaria interpretar os bytes como texto da plataforma na entrada e na saída, e num sistema com uma codificação padrão diferente aquilo é exatamente a corrupção que você não vê até o arquivo ser aberto em outro lugar. E lembre da conta de tamanho quando planejar armazenamento: uma imagem de 500 KB vira um arquivo de texto de 670 KB, e um vídeo de 1 GB vira 1.33 GB.
Para arquivos grandes demais para caber em memória, a documentação própria do módulo te dá a regra: codifique em chunks que são múltiplos de 57 bytes, porque 57 bytes de dados enchem exatamente uma linha de 76 caracteres, 76 sendo 57 vezes 4 dividido por 3. Faça chunks nessa fronteira e você nunca ganha padding no meio do stream:
use MIME::Base64 qw(encode_base64);
open my $in, "<:raw", $ARGV[0] or die $!;
while (read($in, my $buf, 57 * 10)) {
print encode_base64($buf);
}
close $in;
Cada chunk cai exatamente nas fronteiras de linha, o último, possivelmente curto, chunk carrega o padding final, e o resultado é byte a byte o mesmo que devorar o arquivo inteiro e codificar de uma vez, só que com pegada de memória constante. E quando você não precisa de script nenhum, o one-liner cobre, com o -0777 devorando a entrada e o argumento de string vazia mantendo a saída em uma linha:
perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")' < file > file.b64
E-mail: corpos MIME e anexos
E-mail é onde o Base64 ganhou o nome. O padrão MIME diz que dados que não podem viajar com segurança como texto cru devem ser enviados com Content-Transfer-Encoding: base64, em linhas de no máximo 76 caracteres. Se você monta e-mail com MIME::Lite, a coisa toda é um argumento, e o módulo faz a codificação, a quebra de linha e o cabeçalho por você:
use MIME::Lite;
my $mime = MIME::Lite->new(
From => 'me@example.com',
To => 'you@example.com',
Subject => 'A file',
Type => 'text/plain',
Data => 'The body text.',
);
$mime->attach(
Type => 'application/octet-stream',
Data => $bytes,
Encoding => 'base64',
Filename => 'hello.txt',
);
O argumento Encoding é o gatilho: o MIME::Lite codifica o anexo em Base64 em linhas de 76 caracteres (com o newline simples padrão do módulo; é o transporte de e-mail que os transforma em CRLF) e carimba a parte com o cabeçalho Content-Transfer-Encoding correspondente. O Email::MIME adota a mesma posição e codifica em Base64 qualquer anexo que você entregar como string de dados crus (a documentação dele: "todas as partes criadas nesse estilo são codificadas com base64, só para o caso"). Se você está montando uma mensagem MIME crua na mão, o equivalente são as duas linhas da seção de envolvimento, encode_base64($bytes, "\r\n") mais a linha de cabeçalho, e essa é a história inteira do lado do protocolo.
Bancos de dados, config e variáveis de ambiente
Bancos de dados: dados binários frequentemente viajam numa coluna TEXT como Base64, porque a coluna não pode prometer que deixa bytes arbitrários passarem intactos. Guarde a forma em uma linha, nunca a quebrada, ou o seu próximo SELECT vai devolver uma string com newlines no meio do valor:
use MIME::Base64 qw(encode_base64);
# $dbh é um handle DBI já conectado
my $stmt = $dbh->prepare(q{UPDATE photos SET data = ? WHERE id = ?});
$stmt->execute(encode_base64($bytes, ""), 42);
Arquivos de configuração têm a mesma forma: um documento JSON onde o campo binário ou secreto é uma string Base64 em uma linha só, e é exatamente por isso que o segundo argumento existe:
use JSON::PP qw(encode_json);
my $config = {
api_key => encode_base64($key_bytes, ""),
logo_png => encode_base64($png_bytes, ""),
};
open my $fh, ">:raw", "app.json" or die $!;
print {$fh} encode_json($config);
close $fh;
Variáveis de ambiente merecem uma palavra de aviso. Base64 é bom para tokens pequenos no ambiente, mas a forma codificada fica 33 por cento maior que o original, e o sistema operacional limita cada argumento. No Linux o limite é 128 KB por string, imposto pelo execve, e um blob grande numa variável de ambiente não falha com educação: o processo filho morre com um erro obscuro no momento em que é criado. Valores pequenos no ambiente, valores grandes num arquivo ou num banco de dados.
Performance: quem faz o trabalho é o C, não o Perl
O módulo core é implementado em C, e esse C descende de código escrito para o metamail em 1991, o que é um fato curioso até você notar a implicação: o codificador tem três décadas de ajuste. Numa máquina moderna ele processa dados a um ritmo de gigabytes por segundo, que é mais rápido que o disco ou a rede que ele normalmente está alimentando, então o próprio Base64 quase nunca é o gargalo. O I/O é.
Para o sistema raro sem compilador C, o gêmeo puro em Perl, MIME::Base64::Perl, no CPAN, oferece a mesma interface básica, algumas vezes mais lento, mas ainda confortável para cargas de trabalho comuns. E dois hábitos mantêm os trabalhos grandes previsíveis: faça stream com chunks de 57 bytes em vez de devorar, e dimensione seus buffers com encoded_base64_length antes de alocar, o que te poupa tanto o chute quanto a realocação.
Armadilhas, ranqueadas pelo custo da tarde
As armadilhas, mais ou menos na ordem em que mordem:
| Armadilha | O que acontece | Correção |
|---|---|---|
| Esquecer o segundo argumento | a saída chega quebrada em 76 caracteres com um newline no final, e a sua URL, campo JSON ou cabeçalho quebra no meio do valor | passe "" para saída em uma linha, e mantenha a quebra de linha para destinos que esperam |
| Quebrar com a largura errada | um consumidor PEM espera linhas de 64 caracteres e ganha 76, ou um corpo MIME passa do limite de 76 caracteres | combine a largura com o dialeto: "" para nenhum, "\r\n" para MIME, um helper para PEM |
| O croak de caractere largo | uma string de caracteres com códigos acima de 255 morre com Wide character in subroutine entry no meio do pedido | passa os caracteres pelo Encode primeiro, nomeado de propósito, antes da chamada encode_base64 |
| Codificação dupla | re-codificar bytes que já são UTF-8 através de encode("UTF-8", ...) transforma Hëllo em Hëllo |
bytes são codificados exatamente uma vez; confira utf8::is_utf8() na dúvida |
| Misturar alfabetos em silêncio | um valor codificado com - e _ bate num decodificador de alfabeto padrão e volta como lixo |
um dialeto por valor, de ponta a ponta: escolha base64url ou padrão na fronteira |
| O fim de linha no final | encode_base64 anexa o eol mesmo quando a última linha está exatamente cheia, e um consumidor estrito vê uma linha em branco |
chomp ou rtrim no resultado quando o consumidor é exigente |
| Valores envoltos num banco de dados | newlines caem dentro de uma coluna TEXT e o próximo SELECT devolve um token quebrado |
guarde a forma em uma linha; quebre só no destino |
| Variáveis de ambiente com blobs grandes | o crescimento de 33 por cento mais o limite por argumento do sistema operacional mata o processo filho no spawn com um erro obscuro | valores pequenos no ambiente, valores grandes num arquivo ou num banco de dados |
| Pressupor que Base64 é proteção | o formato não esconde nada, e o registro público documenta incidentes reais em que um usuário colou uma troca IMAP e revelou uma senha sem querer | trate a saída como confidencial desde o momento em que ela é produzida, e mantenha ela fora de logs |
| Planejar sem a conta de tamanho | uma imagem de 500 KB vira 670 KB de texto, e o limite de armazenamento ou payload que você não conferiu morde | reserve 4/3 do tamanho original antes de se comprometer |
Uma história contada pelo codificador
O codificador Base64 do Perl tem uma carreira que vale um minuto, e ela começa na primeira toolkit web:
- Nascido no libwww perl. O codificador começou a vida como
LWP::Base64, escrito por Martijn Koster e Joerg Reichelt, que o Gisle Aas absorveu no libwww perl comoMIME::Base64; ele se formou na própria distribuição do CPAN em abril de 1997, versão 2.00, com uma entrada no changelog que diz simplesmente que é baseado no libwww perl 5.08. - A era da velocidade. A versão 2.07 em 1998 embarcou uma implementação C mais rápida e mais inteligente do decodificador, mais ou menos 25 por cento mais veloz nas caixas Linux modernas da época, e o ajuste continuou por uma década.
- A era do Unicode. O Perl 5.8 em 2002 trouxe caracteres com códigos acima de 255 para strings comuns, e o módulo respondeu em etapas: o 2.12 em 2001 rebaixava strings UTF-8 antes de codificar, e o croak moderno de Wide character in subroutine entry é o jeito do codificador de manter aquela promessa. A sincronização 2.13 com o core naquele mesmo ano trouxe o suporte EBCDIC junto, um lembrete de que o Base64 no Perl ainda roda em mainframes.
- A era da linha de comando. Releases do 2.14 em 2003 ao 3.05 em 2004 incluíam um comando
encode-base64de verdade, junto com os gêmeos dele de decode e quoted-printable; o 3.06 em 2005 moveu os scripts para a distribuição separada MIME Base64 Scripts. - A chegada do URL-safe. A RFC 4648 padronizou o alfabeto URL-safe em 2006, um módulo standalone
MIME::Base64::URLSafeapareceu no mesmo ano, e o módulo core alcançou em 3.11 em 2010 com oencode_base64urlem uma única chamada. - A linha moderna. A versão 3.16 em 2020 refez o empacotamento e subiu o piso para o Perl 5.6; os Perls core atuais embarcam a série 3.16, e o módulo é mantido dentro da distribuição core, que é praticamente o lar mais seguro que um módulo core pode ter.
Fatos curiosos, especificamente do Perl
As curiosidades que fazem desta história uma boa história:
- O exemplo do POD é uma frase mágica. Desde 1997, a documentação própria do módulo codifica
Aladdin:open sesame, então a stringQWxhZGRpbjpvcGVuIHNlc2FtZQ==é o cartão de visitas do módulo há quase trinta anos. - O fim de linha padrão é o que você provavelmente não esperava. É um
\nsimples, não o CRLF que o MIME fala. A própria convenção da RFC precisa do segundo argumento, e o módulo embarca com o padrão do programador, não o do protocolo. - A string vazia tem uma regra especial. Codifique nada e você recebe nada de volta, sem newline anexado: a única exceção documentada à regra do eol no final, e a razão pela qual um arquivo vazio vai e volta certinho.
- O primo IMAP tem uma vírgula. A variante de nome de mailbox da RFC 3501 troca o
/por uma vírgula no alfabeto, então uma string Base64 de um servidor IMAP pode conter uma letra que o decodificador padrão trata como ruído. - A linhagem de 1991 é real. A implementação C descende do metamail, o programa de e-mail da Bellcore de 1991, três anos antes de o Perl 5 nascer, então cada chamada de
encode_base64é em parte código dos anos 90. - O gêmeo puro em Perl tem a própria história. Quando a versão 3.00 em 2004 removeu as implementações puras em Perl do módulo core, o changelog as chamou de gordura que esconde problemas de verdade nas implementações XS e as re-lançou como
MIME::Base64::Perl, onde elas ainda moram.
Então da próxima vez que bytes crus precisarem viajar por um mundo só de texto, você sabe a história inteira. Uma chamada de função faz o trabalho, o newline escondido é uma decisão que você toma com o segundo argumento, o croak de caractere largo é o jeito do codificador de manter o seu Unicode honesto, o dialeto URL-safe é uma chamada desde 2010, arquivos fazem stream em chunks de 57 bytes, e a conta de 33 por cento é o preço da entrada. E se um dia você precisar fazer a viagem na direção oposta, pegar uma string de letras e trazer os bytes originais de volta, o artigo relacionado sobre decodificação Base64 em Perl, linkado abaixo, cobre esse ritual na mesma profundidade.
Última atualização: 2026-09-08
Artigo relacionado: Decodificação Base64 em Perl: um guia completo