Tabela de prefixos
Cada linha foi conferida codificando os bytes mágicos reais do formato. O prefixo listado é a parte que nunca muda naquele formato. Os caracteres seguintes dependem do resto do arquivo.
| Prefixo Base64 | Bytes mágicos (hex) | Formato | Abrir com |
|---|---|---|---|
iVBORw0KGgo | 89 50 4E 47 0D 0A 1A 0A | PNG | Base64 para PNG |
/9j/ | FF D8 FF | JPEG | Base64 para JPG |
R0lGOD | 47 49 46 38 (GIF8) | GIF (R0lGODlh = GIF89a, R0lGODdh = GIF87a) | Base64 para GIF |
UklGR | 52 49 46 46 (RIFF) | WebP, WAV, AVI | Base64 para WebP, Base64 para WAV |
JVBERi0 | 25 50 44 46 2D (%PDF-) | Base64 para PDF | |
UEsDB | 50 4B 03 04 (PK..) | ZIP, DOCX, XLSX, PPTX, JAR, APK, EPUB | Base64 para ZIP, Word, Excel |
H4sI | 1F 8B 08 | gzip | Decodificar Base64 com gzip |
eJ, eN, eF, eA | 78 9C, 78 DA, 78 5E, 78 01 | zlib | Zlib e Base64 |
eyJ | 7B 22 ({") | Objeto JSON, JWT | Base64 para JSON, decodificador de JWT |
PD94bWwg | 3C 3F 78 6D 6C 20 (<?xml ) | XML, SVG com declaração | Base64 para XML |
PHN2Zy | 3C 73 76 67 20 (<svg ) | SVG sem declaração (<svg seguido de quebra de linha dá PHN2Zw) | Base64 para SVG |
MII | 30 82 | DER: certificados, chaves, CSRs | decodificador de certificados |
AAAA | 00 00 00 | Blob de chave pública SSH, ou zeros à esquerda | fingerprint de chave SSH |
SUQz | 49 44 33 (ID3) | MP3 com tag ID3v2 | Base64 para MP3 |
AAABAA | 00 00 01 00 | ICO | Base64 para ICO |
d09GMg | 77 4F 46 32 00 (wOF2) | Fonte WOFF2 com contornos TrueType (fontes CFF começam com d09GMk9UVE8) | Base64 para fonte |
TVqQ | 4D 5A 90 (MZ) | EXE ou DLL do Windows (alguns linkers gravam MZP, que dá TVpQ) | Base64 para arquivo |
f0VMR | 7F 45 4C 46 (.ELF) | Binário ELF do Linux | Base64 para arquivo |
Algumas linhas merecem uma observação:
- RIFF é um contêiner. O nome do formato fica nos bytes 8 a 11, e os bytes 9 a 11 viram do 13º ao 16º caractere:
RUJQpara WebP (EBP),QVZFpara WAV (AVE),Vkkgpara AVI (VI). A primeira letra do nome do formato divide um grupo de 3 bytes com o tamanho do arquivo, então não gera um caractere estável. - zlib não tem um prefixo único. O segundo byte registra o nível de compressão:
eJpara o nível padrão,eNpara os níveis 7 a 9,eFpara 2 a 5,eApara 0 e 1 (como gravado pelozlibdo Python). - JSON só começa com
eyJquando os dois primeiros caracteres são{"sem espaço.{ "a": 1 }começa comeyAi, um{formatado seguido de quebra de linha começa comewo, e um array[começa comW. - DER: as estruturas começam com a tag SEQUENCE
30, que sempre dáM. Um comprimento de dois bytes (30 82) dáMII, e o caractere seguinte acompanha o comprimento: uma chave pública RSA 2048 éMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA, um certificado típico éMIIC,MIIDouMIIE. Estruturas curtas usam comprimento de um byte: uma chave privada Ed25519 começa comMC4C, uma chave pública P-256 comMFkw. - Chaves públicas SSH começam com um comprimento big-endian de 4 bytes, então o blob começa com
AAAA. Os caracteres seguintes soletram o tipo da chave:AAAAC3NzaC1lZDI1NTE5éssh-ed25519,AAAAB3NzaC1yc2Eéssh-rsa,AAAAE2VjZHNhéecdsa. Fora do SSH, uma sequência deAsignifica apenas bytes zero. - MP3 sem tag ID3 começa direto com um cabeçalho de frame. Os bits de sincronização
FF Fxgeram um//inicial, por exemplo//uQparaFF FB 90. - Base64URL substitui
+e/, então o prefixo do JPEG vira_9j_. Os outros prefixos da tabela não contêm nenhum dos dois e ficam iguais nos dois alfabetos.
Por que o prefixo é estável
O Base64 trabalha com grupos de 3 bytes. Cada grupo de 3 bytes vira exatamente 4 caracteres, e esses 4 caracteres dependem só desses 3 bytes. Nada do restante do arquivo vaza para trás. Então, se um formato sempre começa com os mesmos 3 bytes, o Base64 dele sempre começa com os mesmos 4 caracteres.
bytes: FF D8 FF
bits: 11111111 11011000 11111111
groups: 111111 111101 100011 111111
index: 63 61 35 63
chars: / 9 j /
Quando o valor mágico fixo não tem um comprimento múltiplo de 3 bytes, o último grupo mistura bits conhecidos com bits do resto do arquivo. Veja o %PDF-, de 5 bytes. Os 3 primeiros bytes dão 4 caracteres estáveis (JVBE). Os 2 bytes seguintes dão mais 2 (Ri), e o sétimo caractere mistura os últimos 4 bits do - com os 2 primeiros bits do dígito da versão. Todo dígito começa com os bits 00, então na prática esse caractere é sempre 0. Depois de JVBERi0 a versão aparece: JVBERi0xLjQ é %PDF-1.4, JVBERi0xLjc é %PDF-1.7, JVBERi0yLjA é %PDF-2.0.
A assinatura PNG tem 8 bytes, o que dá 10 caracteres estáveis. O décimo primeiro mistura o último byte da assinatura com o primeiro byte do comprimento do chunk IHDR, que é sempre 0, então iVBORw0KGgo se mantém. Como todo PNG continua com esse chunk IHDR, os PNGs reais compartilham um prefixo ainda maior: iVBORw0KGgoAAAANSUhEUg.
Quando o prefixo se desloca
O prefixo só funciona quando os bytes mágicos estão no deslocamento 0, 3, 6 e assim por diante. Acrescente um byte na frente e todos os grupos se deslocam, então os caracteres mudam completamente:
| Entrada | O Base64 começa com |
|---|---|
| Assinatura PNG | iVBORw0KGgoAAAA |
| 1 byte + assinatura PNG | AIlQTkcNChoKAAAA |
| 2 bytes + assinatura PNG | AACJUE5HDQoaCgAA |
| 3 bytes + assinatura PNG | AAAAiVBORw0KGgoA |
Com 3 bytes extras o alinhamento volta e o prefixo conhecido reaparece 4 caracteres depois. Motivos comuns para um prefixo deslocado ou ausente:
- Uma marca de ordem de bytes (BOM). Texto UTF-8 com BOM começa com
77u/(EF BB BF). O BOM tem 3 bytes, então um JSON depois dele ainda mostraeyJ:77u/eyJ…. Texto UTF-16 com BOM começa com//4ou//5(little-endian) ou/v8(big-endian). - Texto UTF-16. Texto salvo como UTF-16LE coloca um byte zero depois de cada caractere ASCII, o que produz muitos
A:HelloviraSABlAGwAbABvAA==. O-EncodedCommanddo PowerShell tem essa cara. Use o decodificador de PowerShell ou Base64 para UTF-16. - Compressão ou criptografia. Um PNG compactado com gzip começa com
H4sI; um arquivo criptografado não tem prefixo reconhecível nenhum. - Um data URI.
data:image/png;base64,é um cabeçalho; o Base64 começa depois da vírgula. O parser de data URI separa as duas partes para você. - Quebras de linha e espaços em branco. Eles não deslocam os bytes, mas uma busca de prefixo com
startswithfalha se a string começa com uma quebra de linha ou aspas. Remova-os antes, ou passe a string pelo Reparar Base64.
Verificando um prefixo no código
Dá para comparar os primeiros caracteres como strings, mas comparar os bytes decodificados é menos propenso a erro: os números mágicos vêm direto da especificação de cada formato, e você nunca precisa descobrir quais caracteres são estáveis. Decodificar só os primeiros 12 caracteres dá os primeiros 9 bytes, o suficiente para a maioria das assinaturas:
import base64
SIGNATURES = {
b"\x89PNG\r\n\x1a\n": "png",
b"\xff\xd8\xff": "jpeg",
b"%PDF-": "pdf",
b"PK\x03\x04": "zip",
b"\x1f\x8b": "gzip",
}
def sniff(b64: str) -> str:
head = base64.b64decode(b64.strip()[:12])
for magic, name in SIGNATURES.items():
if head.startswith(magic):
return name
return "unknown"
print(sniff("iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB")) # png
print(sniff("JVBERi0xLjcKJcfsj6IK")) # pdf
A ferramenta Base64 para arquivo faz a mesma verificação com uma lista maior e mostra uma prévia quando o navegador consegue renderizar o formato. Para ver como os grupos de 6 bits viram caracteres, leia como o Base64 funciona e a tabela do alfabeto.
Perguntas frequentes
O que significa uma string Base64 que começa com iVBORw0KGgo?
É uma imagem PNG. iVBORw0KGgo é a forma Base64 da assinatura PNG de 8 bytes 89 50 4E 47 0D 0A 1A 0A. Cole no Base64 para PNG para vê-la.
O que é /9j/ no início de uma string Base64?
Um JPEG. Os bytes FF D8 FF que abrem todo arquivo JPEG viram /9j/ em Base64. Em Base64URL o mesmo prefixo é escrito _9j_.
Por que os JWTs sempre começam com eyJ?
O header de um JWT é um objeto JSON, então começa com {", e esses dois bytes viram eyJ em Base64. A parte do payload começa com eyJ pelo mesmo motivo.
Meu Base64 não bate com nenhum prefixo conhecido. E agora?
Os dados podem ter bytes extras na frente, estar compactados ou criptografados, ou ser texto comum. A ferramenta Base64 para arquivo decodifica os dados e compara os bytes com muitas assinaturas.