Загрузить файл
  1. base64hub
  2. Статьи о Base64
  3. Типичные префиксы Base64
Справочник

Префиксы Base64: как узнать файл по первым символам

Большинство форматов файлов начинаются с фиксированных магических байтов, а значит, и их Base64 начинается с фиксированных символов. Запомните десяток префиксов, и вы отличите PNG от PDF, ещё ничего не декодируя.

Таблица префиксов

Каждая строка проверена кодированием настоящих магических байтов формата. Стабильный префикс здесь означает часть, которая у формата никогда не меняется. Символы после него зависят от остального содержимого файла.

Префикс Base64Магические байты (hex)ФорматЧем открыть
iVBORw0KGgo89 50 4E 47 0D 0A 1A 0APNGBase64 в PNG
/9j/FF D8 FFJPEGBase64 в JPG
R0lGOD47 49 46 38 (GIF8)GIF (R0lGODlh = GIF89a, R0lGODdh = GIF87a)Base64 в GIF
UklGR52 49 46 46 (RIFF)WebP, WAV, AVIBase64 в WebP, Base64 в WAV
JVBERi025 50 44 46 2D (%PDF-)PDFBase64 в PDF
UEsDB50 4B 03 04 (PK..)ZIP, DOCX, XLSX, PPTX, JAR, APK, EPUBBase64 в ZIP, Word, Excel
H4sI1F 8B 08gzipДекодировать gzip из Base64
eJ, eN, eF, eA78 9C, 78 DA, 78 5E, 78 01zlibZlib и Base64
eyJ7B 22 ({")JSON-объект, JWTBase64 в JSON, декодер JWT
PD94bWwg3C 3F 78 6D 6C 20 (<?xml )XML, SVG с объявлениемBase64 в XML
PHN2Zy3C 73 76 67 20 (<svg )SVG без объявления (<svg с переводом строки даёт PHN2Zw)Base64 в SVG
MII30 82DER: сертификаты, ключи, CSRдекодер сертификатов
AAAA00 00 00Блоб открытого ключа SSH или ведущие нулиотпечаток SSH-ключа
SUQz49 44 33 (ID3)MP3 с тегом ID3v2Base64 в MP3
AAABAA00 00 01 00ICOBase64 в ICO
d09GMg77 4F 46 32 00 (wOF2)Шрифт WOFF2 с контурами TrueType (шрифты CFF начинаются с d09GMk9UVE8)Base64 в шрифт
TVqQ4D 5A 90 (MZ)EXE или DLL для Windows (некоторые компоновщики пишут MZP, это даёт TVpQ)Base64 в файл
f0VMR7F 45 4C 46 (.ELF)Бинарник Linux ELFBase64 в файл

Несколько строк требуют пояснения:

  • RIFF сам по себе только контейнер. Название формата лежит в байтах с 8 по 11, а байты с 9 по 11 кодируются в символы с 13-го по 16-й: RUJQ для WebP (EBP), QVZF для WAV (AVE), Vkkg для AVI (VI ). Первая буква названия формата попадает в одну 3-байтную группу с размером файла, поэтому стабильного символа не даёт.
  • zlib не имеет единого префикса. Второй байт хранит уровень сжатия: eJ для уровня по умолчанию, eN для уровней с 7 по 9, eF для 2–5, eA для 0 и 1 (так пишет zlib в Python).
  • JSON начинается с eyJ, только если первые два символа {" идут без пробела. { "a": 1 } начинается с eyAi, отформатированный { с переводом строки даёт ewo, а массив [ даёт W.
  • DER-структуры начинаются с тега SEQUENCE 30, и он всегда даёт M. Двухбайтная длина (30 82) даёт MII, а следующий символ зависит от длины: открытый ключ RSA 2048 начинается с MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA, типичный сертификат с MIIC, MIID или MIIE. Короткие структуры используют однобайтную длину: закрытый ключ Ed25519 начинается с MC4C, открытый ключ P-256 с MFkw.
  • Открытые ключи SSH начинаются с 4-байтной длины в big-endian, поэтому блоб начинается с AAAA. Следующие символы кодируют тип ключа: AAAAC3NzaC1lZDI1NTE5 у ssh-ed25519, AAAAB3NzaC1yc2E у ssh-rsa, AAAAE2VjZHNh у ecdsa. Вне SSH череда A означает просто нулевые байты.
  • MP3 без тега ID3 начинается сразу с заголовка фрейма. Его биты синхронизации FF Fx дают ведущие //, например //uQ для FF FB 90.
  • Base64URL заменяет + и /, поэтому префикс JPEG превращается в _9j_. Остальные префиксы из таблицы не содержат ни одного из этих символов и в обоих алфавитах выглядят одинаково.

Почему префикс стабилен

Base64 работает с группами по 3 байта. Каждая группа из 3 байтов превращается ровно в 4 символа, и эти 4 символа зависят только от этих 3 байтов. Ничего из дальнейшей части файла назад не просачивается. Поэтому если формат всегда начинается с одних и тех же 3 байтов, его Base64 всегда начинается с одних и тех же 4 символов.

bytes:   FF       D8       FF
bits:    11111111 11011000 11111111
groups:  111111 111101 100011 111111
index:   63     61     35     63
chars:   /      9      j      /

Если длина фиксированной сигнатуры не кратна 3 байтам, последняя группа смешивает известные биты с битами из остальной части файла. Возьмём 5-байтный %PDF-. Первые 3 байта дают 4 стабильных символа (JVBE). Следующие 2 байта дают ещё 2 (Ri), а седьмой символ смешивает последние 4 бита - с первыми 2 битами цифры версии. Любая цифра начинается с битов 00, поэтому на практике этот символ всегда 0. После JVBERi0 уже видна версия: JVBERi0xLjQ (%PDF-1.4), JVBERi0xLjc (%PDF-1.7), JVBERi0yLjA (%PDF-2.0).

Сигнатура PNG длиной 8 байт даёт 10 стабильных символов. Одиннадцатый смешивает последний байт сигнатуры с первым байтом длины фрагмента IHDR, а он всегда равен 0, поэтому iVBORw0KGgo сохраняется. Поскольку за сигнатурой в любом PNG идёт этот фрагмент IHDR, у настоящих PNG общий префикс ещё длиннее: iVBORw0KGgoAAAANSUhEUg.

Когда префикс сдвигается

Префикс работает, только если магические байты стоят со смещением 0, 3, 6 и так далее. Добавьте в начало один байт, и все группы сдвинутся, а символы изменятся полностью:

ВводBase64 начинается с
Сигнатура PNGiVBORw0KGgoAAAA
1 байт + сигнатура PNGAIlQTkcNChoKAAAA
2 байта + сигнатура PNGAACJUE5HDQoaCgAA
3 байта + сигнатура PNGAAAAiVBORw0KGgoA

С 3 лишними байтами выравнивание восстанавливается, и знакомый префикс появляется на 4 символа позже. Типичные причины сдвинутого или пропавшего префикса:

  • Метка порядка байтов (BOM). Текст UTF-8 с BOM начинается с 77u/ (EF BB BF). BOM занимает 3 байта, поэтому JSON после него всё равно даёт eyJ: 77u/eyJ…. Текст UTF-16 с BOM начинается с //4 или //5 (little-endian) либо с /v8 (big-endian).
  • Текст в UTF-16. Текст в UTF-16LE ставит нулевой байт после каждого ASCII-символа, и получается много A: Hello превращается в SABlAGwAbABvAA==. Так выглядит -EncodedCommand в PowerShell. Используйте декодер PowerShell или Base64 в UTF-16.
  • Сжатие или шифрование. Сжатый gzip-ом PNG начинается с H4sI, а у зашифрованного файла узнаваемого префикса нет вовсе.
  • Data URI. data:image/png;base64, служит заголовком, Base64 начинается после запятой. Парсер data URI разделит их за вас.
  • Переносы строк и пробелы. Байты они не сдвигают, но поиск префикса через startswith не сработает, если строка начинается с перевода строки или кавычки. Сначала обрежьте лишнее или прогоните строку через починку Base64.

Проверка префикса в коде

Можно сравнивать первые символы как строки, но сравнивать декодированные байты надёжнее: магические числа берутся прямо из спецификации каждого формата, и не нужно вычислять, какие символы стабильны. Декодирование первых 12 символов даёт первые 9 байтов, а этого хватает для большинства сигнатур:

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

Инструмент Base64 в файл делает ту же проверку по более длинному списку и показывает превью, если браузер умеет отображать формат. Как 6-битные группы соответствуют символам, рассказано в статье как работает Base64 и показано в таблице алфавита.

Вопросы и ответы

Что значит строка Base64, которая начинается с iVBORw0KGgo?

Это PNG-картинка. iVBORw0KGgo кодирует 8-байтную сигнатуру PNG 89 50 4E 47 0D 0A 1A 0A. Вставьте строку в Base64 в PNG, чтобы её увидеть.

Что такое /9j/ в начале строки Base64?

Это JPEG. Байты FF D8 FF, с которых начинается любой JPEG-файл, кодируются в /9j/. В Base64URL тот же префикс выглядит как _9j_.

Откуда у JWT префикс eyJ?

Токен открывается заголовком в виде JSON-объекта, а первые байты {" в Base64 дают eyJ. Полезная нагрузка тоже объект JSON, поэтому и она начинается с eyJ.

Моя строка Base64 не похожа ни на один известный префикс. Что делать?

Возможно, перед данными есть лишние байты, данные сжаты или зашифрованы, или это обычный текст. Инструмент Base64 в файл сверяет декодированные байты со множеством сигнатур, а не только с первыми символами.

Читайте также