Você tem que lidar com o formato Base64? Então esse site é perfeito para você! Use o nosso ferramenta online super útil para codificar ou decodificar os seus dados.

Codificação Base64 em Ruby: um guia completo

Seus dados têm um destino que não aceita o jeito que eles aparecem. Uma imagem que precisa morar dentro de um documento JSON. Um token que precisa cruzar uma URL. Um anexo que precisa sobreviver a um protocolo desenhado para texto de sete bits. Um segredo que precisa ficar numa variável de ambiente sem quebrar as aspas. Em cada um desses lugares, algo entre aqui e lá está prestes a destruir o seu binário - e a solução tem nome: Base64.

Em Ruby, o trabalho inteiro mora num módulo que vem com a linguagem. Três codificadores, nada para instalar, e uma saída que dá para prever caractere por caractere antes mesmo de rodar o código. Essa previsibilidade é a metade da história que a maioria dos guias pula, porque é no encoding que as surpresas cobram a conta: um newline final se infiltra no seu JSON, uma quebra de linha não pedida parte um token em dois, e uma escolha errada de alfabeto arruína uma URL. Este guia percorre os três codificadores, a matemática da saída e cada payload que um desenvolvedor Ruby de verdade codifica, para as surpresas deixarem de ser surpresas.

Uma revisão rápida antes de começar: o Base64 reescreve dados três bytes por vez, emitindo quatro caracteres de um alfabeto de 64 símbolos, com um ou dois caracteres = de padding quando a entrada não divide por três certinho - que também é o motivo de a saída acabar mais ou menos um terço maior que a entrada. A página inicial deste site cobre o formato a fundo, então este artigo mantém a conversa sobre formato num sopro só e vai direto ao trabalho.

Qual codificador você precisa?

O Ruby te dá três codificadores, e a escolha entre eles é um quiz de três perguntas: a saída pode conter quebras de linha? Pode conter + ou /? Pode conter padding? Aqui está o elenco inteiro:

Codificador Formato da saída Quebras de linha Padding Recorra a ele quando
Base64.strict_encode64(bin) uma linha, alfabeto padrão nunca sempre presente JSON, tokens, APIs, arquivos - o padrão seguro
Base64.encode64(bin) várias linhas, alfabeto padrão a cada 60 caracteres, mais uma no final sempre presente corpos de e-mail e outros protocolos de texto orientados a linhas
Base64.urlsafe_encode64(bin, padding: true) uma linha, alfabeto de hífen-underscore nunca a sua escolha, ligado por padrão qualquer coisa que caia numa URL, cookie ou identificador

Se você está decidindo sob pressão de tempo, a resposta curta é: strict_encode64 por padrão, urlsafe_encode64 quando o resultado vai viajar dentro de uma URL, e encode64 só quando o lado receptor é um protocolo de texto que pede linhas curtas. Tudo abaixo explica o porquê, e onde cada escolha te custa em silêncio.

strict_encode64: o cavalo de batalha

Base64.strict_encode64 é o codificador que você vai usar de verdade na grande maioria do seu código. Ele produz exatamente uma linha de saída, sempre com o padding correto, do alfabeto padrão:

require "base64"
Base64.strict_encode64("hello world")
# => "aGVsbG8gd29ybGQ="
Base64.strict_encode64("s")
# => "cw=="

E, como o algoritmo é determinístico, dá para prever o comprimento exato da saída a partir da entrada - sem chute, sem bugs off-by-one nas suas colunas de banco de dados. A tabela abaixo é a aritmética inteira:

Comprimento da entrada Comprimento da saída Padding no final
3n bytes (divide certinho) 4n caracteres nenhum
3n + 1 bytes 4n + 4 caracteres dois =
3n + 2 bytes 4n + 4 caracteres um =

Então 11 bytes viram 16 caracteres, 100 bytes viram 136, e um arquivo de 1 megabyte vira uns 1.33 megabytes de texto. Esse crescimento de um terço é o preço de entrada de todo payload Base64 que você envia, e é o número para guardar no bolso sempre que uma coluna, um cache ou um limite de taxa de API começar a apertar.

Base64.strict_encode64("123")
# => "MTIz"        3 bytes entrando, 4 caracteres saindo
Base64.strict_encode64("1234")
# => "MTIzNA=="   4 bytes entrando, 8 caracteres saindo, dois caracteres de padding
Base64.strict_encode64("12345")
# => "MTIzNDU="   5 bytes entrando, 8 caracteres saindo, um caractere de padding

encode64: o que adiciona newlines

Base64.encode64 é o clássico, e ele tem um comportamento que já encerrou mais de uma tarde: ele quebra a saída. A cada 60 caracteres, ele começa uma nova linha, e sempre termina com uma quebra de linha final:

Base64.encode64("hello world")
# => "aGVsbG8gd29ybGQ=\n"
Base64.encode64("*" * 46)
# => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq\nKg==\n"

A quebra não é bug - é um recurso herdado do lar original do método no mundo MIME, onde linhas longas eram violação de protocolo. A gem mail do Ruby conta com isso de propósito - seu codificador Base64 até carrega um comentário no sentido de que a quebra de linhas do Ruby mantém a saída dentro dos limites de comprimento de linha do SMTP. Se você está codificando corpos de e-mail, o encode64 está te fazendo um favor.

Mas em todos os outros contextos, a quebra é um imposto. O acidente mais comum é um documento JSON onde um valor Base64 de repente ocupa duas linhas:

payload = { "logo" => Base64.encode64(File.binread("logo.png")) }
puts payload.to_json
# o valor do logo carrega quebras de linha que ninguém pediu

E o irmão menor do mesmo bug é o newline final em strings curtas: Base64.encode64("s") devolve "cw==\n", então um token que você cola numa URL ou compara com um valor esperado falha por motivos que você vai gastar vinte minutos rastreando. A cura é um strip - mas a cura melhor é strict_encode64, que nunca adiciona um único caractere que você não conquistou. Há também uma assimetria charmosa que vale saber: uma entrada vazia produz uma string vazia sem newline final, então Base64.encode64("") é só "".

urlsafe_encode64: o alfabeto seguro para links

Dois caracteres do alfabeto padrão causam problema em qualquer lugar que um parser de URL esteja de olho: + (um espaço, em query strings) e / (um separador de caminho). A RFC 4648 resolveu com uma troca - - toma o lugar de +, _ toma o lugar de / - e o Ruby implementa no Base64.urlsafe_encode64:

Base64.urlsafe_encode64("\xfb\xef\xbe".b)
# => "----"
Base64.urlsafe_encode64("\xff\xff\xff".b)
# => "____"

Esses dois exemplos são o alfabeto em exposição: os mesmos bytes que o codificador padrão renderiza como ++++ ou //// saem como ---- e ____, caracteres que sobrevivem a URLs, caminhos, nomes de arquivo e campos de formulário sem qualquer percent-encoding. A saída é uma linha, igual ao strict_encode64.

A única opção do método é a keyword padding:, adicionada no Ruby 2.3, e é ela que você precisa conhecer. A especificação do JSON Web Token pede base64url sem padding, e muitos outros esquemas de token também:

Base64.urlsafe_encode64("*")
# => "Kg=="
Base64.urlsafe_encode64("*", padding: false)
# => "Kg"

Com o padding desligado, a conta de comprimento muda: 3n + 1 bytes agora rendem 4n + 2 caracteres e 3n + 2 bytes rendem 4n + 3. O lado do decodificador dá conta - o urlsafe_decode64 do Ruby adiciona o padding que falta ele mesmo - então saída sem padding é segura para emitir, mas saída com padding é o padrão mais amigável quando o outro lado é um leitor estrito da RFC 2045. Um cuidado: desligue o padding só quando uma especificação pedir. Ele economiza um ou dois caracteres, mas custa uma classe de reclamações de decodificador.

O que o Ruby realmente codifica: strings são bytes

Antes dos casos de uso, um fato específico do Ruby que molda tudo: uma string do Ruby é uma sequência de bytes vestindo uma etiqueta de encoding, e os codificadores olham só para os bytes. A etiqueta diz ao Ruby como exibir e comparar a string; ela não muda o que é codificado:

require "base64"
s = "h\u{e9}llo"
puts s.encoding
# => UTF-8
puts s.bytes.length
# => 6   o e acentuado é dois bytes
Base64.strict_encode64(s)
# => "aMOpbGxv"

Essa é a armadilha por trás do "por que a minha saída está mais longa do que eu esperava": a string que você digitou costuma ser mais curta em caracteres do que em bytes, e o Base64 cobra por byte. A direção inversa é igualmente silenciosa - uma string UTF-8 inválida é codificada sem nenhuma reclamação, porque o codificador não tem nada para validar:

broken = "h\u{e9}llo".b.force_encoding("UTF-8")
broken.setbyte(1, 0xFF)
puts broken.valid_encoding?
# => false
Base64.strict_encode64(broken)
# => algum base64, sem erro, bytes são bytes

Para binário de verdade, pule todo o mecanismo de texto e monte bytes com pack ou leia-os com File.binread. Um exemplo satisfatório é a assinatura PNG - os oito bytes que abrem todo arquivo PNG na Terra:

png_magic = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A].pack("C*")
Base64.strict_encode64(png_magic)
# => "iVBORw0KGgo="

JWTs: assinando dados que também são legíveis

JSON Web Tokens são o consumidor de maior perfil do codificador URL-safe do Ruby. Um token é feito de três segmentos base64url juntados por pontos - header, payload, assinatura - e a especificação é explícita: o alfabeto precisa ser o URL-safe, e o padding precisa estar desligado. A gem jwt cuida de tudo isso:

# no Gemfile: gem "jwt"
require "jwt"
token = JWT.encode(
  { sub: "1234567890", name: "Alice", exp: Time.now.to_i + 3600 },
  "my-secret-key",
  "HS256"
)
puts token
# => eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIi...
payload, header = JWT.decode(token, "my-secret-key", true, algorithm: "HS256")
puts header
# => {"alg"=>"HS256"}

Você também pode ver a camada Base64 fazendo o trabalho dela dentro do token, porque os segmentos são só base64url de JSON:

require "base64"
require "json"
payload_json = JSON.generate({ "sub" => "1234567890", "name" => "Alice" })
segment = Base64.urlsafe_encode64(payload_json, padding: false)
puts segment
# => eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIn0

Duas regras pertencem a este caso de uso. Nunca monte seu próprio JWT na mão em produção - a assinatura é o que faz um token ser qualquer coisa além de uma confissão - e quando decodificar com a gem, fixe o algoritmo no hash de opções como mostrado acima, para que o próprio header do token não possa escolher o método de verificação por você.

HTTP Basic Auth: montando o cabeçalho

O jeito mais antigo de dizer "quem sou eu" no HTTP ainda é o mais simples: coloque as credenciais em Base64, coloque-as depois da palavra Basic e envie o cabeçalho. Montá-lo no Ruby é uma linha:

require "base64"
credentials = Base64.strict_encode64("alice:s3cr3t!")
puts "Basic #{credentials}"
# => Basic YWxpY2U6czNjcjN0IQ==

A biblioteca padrão do Ruby faz exatamente isso por você no Net::HTTP, chamando o template core pack diretamente - ["user:pass"].pack("m0") é o que basic_auth se reduz por baixo do capô:

require "net/http"
request = Net::HTTP::Get.new("https://example.org/api")
request.basic_auth("alice", "s3cr3t!")
puts request["Authorization"]
# => Basic YWxpY2U6czNjcjN0IQ==

E a ressalva de segurança, dita uma vez para constar: Base64 é um tradutor, não uma fechadura. As credenciais num cabeçalho Basic auth são legíveis por qualquer pessoa que consiga ler o pacote. Este cabeçalho só é aceitável sobre HTTPS, onde o transporte faz a proteção de verdade.

Data URIs: imagens e fontes embutidas

Um data URI é a resposta da web para "quero esta imagem sem um arquivo separado": um media type, a palavra base64, uma vírgula e os bytes. É assim que demos HTML de arquivo único enviam seus logos, que favicons se escondem dentro de CSS e que uma imagem gerada pode morar inteiramente numa string de template:

require "base64"
png = File.binread("logo.png")
data_uri = "data:image/png;base64,#{Base64.strict_encode64(png)}"
css = "background-image: url(#{data_uri});"
puts css.length
# => o seu stylesheet, menos um request HTTP

Use strict_encode64 aqui - o payload é uma linha limpa só, sem quebra, sem newline. E fique de olho no tamanho: a imagem que você embute cresce por volta de um terço, então data URIs brilham para assets pequenos (favicons, logos, fontes de ícone) e incham nos grandes. Uma foto de destaque de dois megabytes vira 2.7 megabytes do seu documento HTML, e seus usuários vão sentir no primeiro scroll em 4G.

E-mail: onde o Base64 nasceu

Todos os outros casos de uso deste artigo são descendentes deste. SMTP foi desenhado nos anos 80 para linhas curtas de texto de sete bits, ou seja, ele não conseguia carregar um JPEG. A correção - Privacy-Enhanced Mail, depois MIME em 1993 - foi reescrever binário como texto com um alfabeto de 64 símbolos, que é exatamente o formato que você usa hoje. As cicatrizes ainda aparecem na saída do Ruby: encode64 quebra em 60 caracteres - uma largura que não serve a nenhum protocolo em particular, como você vai ver mais adiante, mas curta o suficiente para manter o e-mail educado.

Na prática você vai deixar a gem mail fazer o trabalho MIME. Anexe um arquivo binário e a gem escolhe o codificador Base64, quebra as linhas e escreve os cabeçalhos:

# no Gemfile: gem "mail"
require "mail"
message = Mail.new do |m|
  m.from = "dev@example.org"
  m.to = "ops@example.org"
  m.subject = "Binary report"
  m.add_file("report.bin")
end
puts message.encoded
# a parte do anexo carrega Content-Transfer-Encoding: base64

Texto não-ASCII em cabeçalhos recebe o mesmo tratamento num figurino ligeiramente diferente: palavras codificadas RFC 2047, que embrulham Base64 numa tag de charset entre pontos de interrogação, como =?UTF-8?B?w7wgc2VjcmV0cw==?=. Se um dia você montar ou analisar essas na mão, o Base64 por dentro é do tipo comum, decodificado com decode64 e depois re-etiquetado com o charset que a palavra declara.

Armadura PEM para chaves e certificados

Chaves e certificados vestem armadura PEM, e a armadura é Base64 com um aro: uma linha BEGIN, os bytes codificados em linhas de 64 caracteres e uma linha END. Se um dia você precisar produzir um arquivo PEM a partir de bytes DER crus, a construção é um embrulho em duas etapas:

require "base64"
der_bytes = File.binread("server.der")
body_lines = Base64.strict_encode64(der_bytes).scan(/.{1,64}/)
pem = (["-----BEGIN PRIVATE KEY-----"] + body_lines +
  ["-----END PRIVATE KEY-----"]).join("\n") + "\n"
File.write("server.key", pem)

Duas notas. Primeira: você quase nunca vai precisar disso, porque a gem openssl escreve PEM por você (key.to_pem), e o rótulo entre as linhas BEGIN e END precisa casar com a coisa de dentro - errar produz um arquivo que toda ferramenta da internet se recusa a ler. Segunda: o comprimento de linha aqui é 64, a largura clássica do PEM; o encode64 do Ruby quebra em 60 no lugar, e todo parser PEM digno ignora comprimentos de linha por completo, então qualquer largura decodifica bem.

Arquivos: a convenção .b64

O formato de arquivo mais comum do mundo Base64 é um arquivo de texto puro com extensão .b64 (ou .base64) guardando um payload codificado - pense nele como "o arquivo, mas seguro para colar em qualquer lugar". Produzir um a partir do Ruby é um one-liner:

require "base64"
File.write("payload.b64", Base64.strict_encode64(File.binread("payload.bin")))
puts File.size("payload.b64")
# => mais ou menos 1.33 vezes o tamanho original

Use strict_encode64 para o arquivo guardar uma linha limpa só - a convenção que a maioria das ferramentas de decodificação (e o decodificador estrito do Ruby) espera. Ler de volta é o espelho: leia, decodifique e escreva os bytes em modo binário para que nada os mude no caminho de saída:

encoded = File.read("payload.b64")
bytes = Base64.strict_decode64(encoded)
File.binwrite("restored.bin", bytes)

Se os seus arquivos .b64 vêm de ferramentas que quebram linhas - algumas variantes da CLI base64 fazem isso - remova as quebras de linha antes de um decode estrito, ou use o decodificador tolerante, que pula elas de graça.

Arquivos de configuração, variáveis de ambiente e bancos de dados

Sempre que dados binários precisam morar dentro de um documento de texto, o Base64 é a ponte. O padrão se repete em três lugares com pequenas variações.

Variáveis de ambiente e arquivos .env não conseguem guardar bytes crus, então os bytes são codificados antes de saírem da máquina que os tem:

require "base64"
# em algum lugar onde você provisiona o app
ENV["APP_LOGO"] = Base64.strict_encode64(File.binread("logo.png"))
# em algum lugar onde o app sobe
b64 = ENV.fetch("APP_LOGO")
File.binwrite("logo.png", Base64.decode64(b64))

YAML tem um tipo binário nativo, e o Psych cuida do Base64 por você - fazer dump de uma string BINARY para YAML a escreve como um scalar !binary, e ela carrega de volta byte-idêntica:

require "yaml"
yaml_text = YAML.dump({ "logo" => File.binread("logo.png") })
puts yaml_text.lines.first(2)
# => "---"
# => "logo: !binary |-"
data = YAML.load(yaml_text)
puts data["logo"].encoding
# => ASCII-8BIT

Em bancos de dados a questão é tipo de armazenamento, não encoding. Se o seu banco tem uma coluna binária de verdade - BLOB, BYTEA, VARBINARY - use, e deixe o driver carregar os bytes. Base64-em-uma-coluna-TEXT é o padrão para quando a camada de armazenamento só fala strings: alguns document stores, APIs em formato JSON, ou um schema legado que você não pode mudar. O preço é o imposto de um terço no tamanho da coluna, e a disciplina de codificar na entrada e decodificar na saída em cada fronteira, sem exceção.

Checksums que viajam como texto

Hashes são binários, mas checksums viajam quase sempre em texto: listas de integridade de arquivo, chaves de cache, impressões digitais, linhas de log. Cada classe de digest do Ruby tem um método base64digest que faz o encode em uma chamada:

require "digest"
Digest::SHA256.base64digest("hello")
# => "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="

A saída é Base64 padrão com padding - a mesma coisa que você obteria de Base64.strict_encode64(Digest::SHA256.digest("hello")) - então é seguro guardar, comparar e colar. A única decisão é consistência: uma lista de checksums gerada com Base64 precisa ser checada contra saída Base64, e representações em hex e Base64 do mesmo hash são strings diferentes, então escolha uma e fique com ela.

Codificando coisas grandes em pedaços pequenos

Como os decodificadores, os codificadores são baseados em buffer: leem a entrada inteira e emitem a saída inteira. Não existe codificador streaming na biblioteca padrão, então para payloads grandes o plano é memória, e há uma simetria agradável na matemática. Codificar aumenta seus dados em um terço, então a saída - não a entrada - é a sua maior alocação, e para um arquivo de 1 gigabyte você deve esperar mais ou menos 1.33 gigabytes de texto na sua frente.

Se for demais para segurar de uma vez, você pode codificar em pedaços, porque o alfabeto Base64 é auto-sincronizante nas fronteiras de três bytes: codifique cada fatia de 3 bytes independentemente e a concatenação é idêntica a codificar o todo:

require "base64"
require "securerandom"
bin = SecureRandom.random_bytes(10_001)
whole = Base64.strict_encode64(bin)
chunked = bin.scan(/.{1,3}/m).map { |slice| Base64.strict_encode64(slice) }.join
puts chunked == whole
# => true

O mesmo truque te dá um wrapper de linhas feito à mão que casa com encode64 exatamente: 45 bytes sempre codificam para exatamente 60 caracteres, então fatiar a entrada em 45 bytes e juntar os pedaços com quebras de linha reproduz a saída MIME clássica, uma linha de cada vez, com só uma fatia na memória por vez:

def wrap_like_encode64(bin)
  lines = bin.scan(/.{1,45}/m).map { |slice| Base64.strict_encode64(slice) }
  lines.join("\n") + "\n"
end
bin = SecureRandom.random_bytes(10_001)
puts wrap_like_encode64(bin) == Base64.encode64(bin)
# => true

Da linha de comando

Codificar também não precisa de arquivo de script. A forma one-liner lê um arquivo e escreve o Base64 dele no stdout:

ruby -rbase64 -e 'print Base64.strict_encode64(File.binread(ARGV[0]))' payload.bin > payload.b64

E a forma de pipe lê o stdin, que é como você embrulharia um stream de bytes de qualquer outro comando:

some_command | ruby -rbase64 -e 'print Base64.strict_encode64(STDIN.read)'

Mantenha print nos dois - um puts perdido acrescentaria um newline ao seu Base64, e para a saída do strict_encode64 isso transforma um token limpo num quebrado. A mesma regra prática do lado da decodificação: se o próximo consumidor da sua saída é estrito, nada além do próprio Base64 pode viajar junto.

As armadilhas que custaram bytes extras a desenvolvedores Ruby

  • O newline final em JSON. Base64.encode64 termina todo resultado não vazio com uma quebra de linha, então um valor que deveria ser um token limpo chega no seu JSON com um \n de surpresa no final. Use strict_encode64 para tudo que vai ser guardado, comparado ou enviado em uma linha só.
  • A quebra de 60 caracteres em tokens e URLs. O mesmo método quebra saída longa em várias linhas. Uma string quebrada numa URL são duas URLs, e um token quebrado é um quebrado. De novo: strict_encode64, ou strip/delete as quebras de linha se você está preso a saída de encode64.
  • Mais e barra em URLs. Base64 padrão numa query string quer dizer percent-encoding %2B, %2F e %3D na saída e torcer para o outro lado decodificá-los. urlsafe_encode64 remove o problema na fonte.
  • Padding no lugar errado. JWTs e outros esquemas de token querem padding desligado; leitores MIME podem não lidar com padding faltando. Emita padding: false só onde uma especificação pede, e saiba de que lado dessa cerca cada um dos seus consumidores está sentado.
  • Caracteres não são bytes. Uma string de cinco caracteres com uma letra acentuada tem seis bytes em UTF-8, e a conta do comprimento da saída roda em bytes. Quando o resultado codificado está "longo demais", conte bytes, não caracteres.
  • O imposto de um terço no design de schema. Um BLOB de 16 KB vira uma string Base64 de uns 22 KB numa coluna TEXT. Dimensione suas colunas, caches e payloads de API para a forma codificada, não para a forma binária.
  • Dois alfabetos, duas strings diferentes. Os mesmos bytes codificam de forma diferente nos alfabetos padrão e URL-safe, então um valor codificado só é comparável contra outro valor do mesmo alfabeto. Nunca compare ou misture.
  • Base64 não é uma fechadura. Codificar um segredo não o torna secreto. Qualquer pessoa com a string tem os seus dados; o Base64 só controla como os bytes aparecem, não quem pode lê-los.

Hábitos que economizam bytes e bugs

  • Torne strict_encode64 o seu padrão. Troque para urlsafe_encode64 no momento em que a saída vai morar numa URL, cookie ou identificador, e para encode64 só quando o destino é um protocolo de texto orientado a linhas, como e-mail.
  • Mantenha o alfabeto consistente entre o codificador e o decodificador nas duas pontas do fio. O bug "Base64 quebrado" mais comum de todos é um produtor de alfabeto padrão encontrando um consumidor URL-safe, ou o contrário.
  • Alimente os codificadores com bytes que você realmente quer codificar: File.binread para arquivos, pack para binário construído, e uma string UTF-8 quando a string é o dado. O codificador não vai questionar suas escolhas - ele só conta bytes.
  • Orce o crescimento. Sempre que uma string Base64 cruza uma fronteira para um recipiente com tamanho, multiplique por 4/3 e acrescente uma folguinha para padding.
  • Use Base64 para portabilidade, nunca para sigilo. Se o objetivo é manter os dados privados, a ferramenta é criptografia, e o Base64 é só o que você faz com o texto cifrado depois.

Como o Base64 virou uma gem

Por quase toda a sua vida, o módulo Base64 foi só um arquivo na biblioteca padrão, como muitos dos helpers mais antigos do Ruby. Os métodos strict e URL-safe se juntaram ao par original durante a linha de desenvolvimento 1.9 - a biblioteca base64 inteira, com os quatro métodos, foi adicionada ao trunk em setembro de 2008 e lançada pela primeira vez no 1.9.1 (2009), e a keyword padding: chegou com o Ruby 2.3 em 2015. Tudo na API que você vê hoje já tinha se assentado até ali - o resto da história é sobre como o módulo é distribuído.

Em 2020, com o Ruby 3.0, a equipe core começou a extrair bibliotecas padrão para gems próprias, e o base64 virou uma delas: versão 0.1.0, mantida no repositório ruby/base64 pelos contribuidores core. Ele foi distribuído como default gem - entregue com o Ruby e sempre disponível, então require "base64" continuou funcionando com zero cerimônia. A versão 0.2.0 veio com o Ruby 3.3 em 2023, adicionando a constante Base64::VERSION e um conjunto de documentação bem mais rico.

Então o Ruby 3.4 em dezembro de 2024 redesenhou a linha: o base64 saiu da lista de default gems para a lista de bundled gems, a mesma prateleira de csv e drb. Bundled gems ainda vêm com a linguagem, mas projetos baseados em Bundler devem declará-las, então se você está no Ruby 3.4 ou posterior e seu app é guiado por Bundler, adicione gem "base64" ao seu Gemfile (ou rode gem install base64) e você está coberto. O Ruby 4.0 em 2025 trouxe a versão 0.3.0, com assinaturas de tipo RBS para checkers estáticos enxergarem o módulo como ele é.

Em toda a jornada, a implementação continuou o que sempre foi: algumas dúzias de linhas de Ruby puro embrulhadas em volta dos templates core pack e unpack. Sem extensão em C, sem dependências, e - com contagem de downloads na casa das centenas de milhões no rubygems.org - uma das gems mais instaladas da plataforma.

Fatos divertidos do Ruby

  • O módulo inteiro, codificadores incluídos, é curto o suficiente para ser lido numa pausa de café. encode64 é literalmente [bin].pack("m"), strict_encode64 é [bin].pack("m0"), e urlsafe_encode64 é o codificador estrito com uma troca de duas letras por cima, menos o padding quando você pede.
  • A quebra de 60 caracteres do encode64 não casa nem com o máximo de 76 caracteres do MIME nem com o 64 clássico do PEM. É simplesmente o que o template pack m sempre fez, e o codificador Base64 da gem mail comenta isso com aprovação: a quebra automática de linhas do Ruby mantém a saída dentro dos limites do SMTP.
  • O Net::HTTP do Ruby não se dá ao trabalho de usar o módulo Base64 para Basic auth - ele chama o template pack diretamente, o que é um bom lembrete de que o módulo é uma camada de conveniência sobre o core, e não o contrário.
  • Toda classe de digest carrega um método base64digest, então Digest::SHA256.base64digest é cidadão de primeira classe ao lado do hexdigest - checksums em texto sem uma segunda chamada.
  • A tag !binary do YAML é Base64 disfarçado. O Psych faz o encoding no momento em que você faz dump de uma string BINARY, que é o motivo de arquivos de config cheios de binários terem o jeito que têm.
  • O módulo que você usa nem sempre foi o módulo que você lembra. O Ruby antigo tinha b64encode (quebra num comprimento escolhido) e decode_b (decodificação de cabeçalhos RFC 2047); os dois sumiram na linha 1.9, então qualquer código pré-2010 que você herde e que os chame morre com um NoMethodError.
  • Os IDs de vídeo do YouTube são base64url sem padding - onze caracteres, sem mais, sem barra, sem igual - que é exatamente o tipo de identificador curto e seguro para links para o qual o alfabeto URL-safe foi desenhado.

O outro lado

Com isso você tem o quadro completo de codificação: um cavalo de batalha padrão que nunca te surpreende, um clássico que quebra linhas para os protocolos que pedem, um alfabeto seguro para links com um interruptor de padding, e as regras de nível de byte que decidem exatamente como a sua saída vai ficar. A direção inversa - desmontar uma string Base64, escolher entre os três decodificadores do Ruby e transformar os bytes resultantes em algo que dá para usar - tem as suas próprias armadilhas silenciosas, começando por um decodificador que nunca diz não. Esse lado da rua é coberto em profundidade no artigo de decodificação Base64, linkado abaixo.

Última atualização: 2026-09-08

Artigo relacionado: Decodificação Base64 em Ruby: um guia completo