[РУКОВОДСТВО] Что такое Base64 и как он работает?
Практическое объяснение с первых принципов. Познакомьтесь с приёмом группировки по 6 бит, лежащим в основе Base64, узнайте, почему результат на 33% длиннее, откуда берётся паддинг = и когда стоит использовать эту кодировку.
// ОПРЕДЕЛЕНИЕ В ОДНО ПРЕДЛОЖЕНИЕ
Base64 — это текстовое представление двоичных данных, которое использует всего 64 печатных ASCII-символа: A–Z, a–z, 0–9 и два дополнительных знака (обычно + и /, с = в качестве маркера паддинга). И это вся идея целиком. Каждые три байта двоичных данных перегруппируются в четыре символа Base64, больше ничего не происходит.
Это именно кодирование, а не шифрование — любой может развернуть его за миллисекунды. Цель не в приватности, а в безопасной транспортировке. Base64 позволяет разместить двоичные полезные нагрузки (изображения, сертификаты, PDF-файлы, криптографические ключи) там, где допустим только текст: в атрибутах HTML, строках JSON, URL-адресах, телах электронных писем, конфигах YAML, текстовых колонках базы данных, переменных окружения.
// ЗАЧЕМ ОН ВООБЩЕ НУЖЕН
Чисто текстовые каналы вырезают или искажают байты, которые им незнакомы. Почтовый шлюз, понимающий только 7-битный ASCII, сломает старший бит у каждого неанглийского символа. Парсер JSON отвергнет встроенные нулевые байты. URL, в котором встречается буквальный пробел, кавычка или амперсанд, недействителен, пока эти символы не закодированы процентами. Стандарты SMTP 1990-х исходили из того, что тела писем всегда будут простым английским текстом — но людям хотелось пересылать и фотографии, и таблицы.
Base64 решает задачу, ограничивая выходной алфавит 64 символами, которые без потерь проходят через любой известный текстовый канал: 26 прописных букв, 26 строчных, 10 цифр и два символа, которые (а) печатаемы, (б) не используются распространёнными механизмами экранирования и (в) достаточно различимы, чтобы пройти туда и обратно через ASCII. Плюс символ паддинга =, который тоже безопасен.
// ПРИЁМ ГРУППИРОВКИ ПО 6 БИТ
Вот механическое сердце Base64 в одной строке: берём вход как поток битов, разбиваем его на куски по 6 бит и ищем каждый кусок в алфавите из 64 символов.
Почему именно 6 бит? Потому что 2^6 = 64, и каждый 6-битный фрагмент ровно соответствует одному символу алфавита. Почему не 7 и не 8? 7 бит дают 128 значений (слишком много — не все из них печатаемы), а 8 бит — это снова просто сырое двоичное представление. 6 бит — золотая середина, при которой каждому возможному значению соответствует удобочитаемый символ.
Три входных байта = 24 бита = ровно четыре группы по 6 бит = четыре символа Base64. Соотношение 3 на входе к 4 на выходе фиксированное; именно поэтому результат Base64 примерно в 4/3 раза больше входных данных, то есть примерно на 33%.
// Worked example: encoding the 3 bytes 'Man' (77 97 110)
// ASCII: M a n
// Binary: 01001101 01100001 01101110
// Re-group into 6-bit chunks:
// 010011 010110 000101 101110
// Decimal: 19 22 5 46
// Base64: T W F u
// Result: 'TWFu' (stored as 4 ASCII bytes: 84 87 70 117)
// АЛФАВИТ BASE64
RFC 4648 §4 задаёт стандартный алфавит. Позиции 0–25 — это A–Z, 26–51 — a–z, 52–61 — 0–9, позиция 62 — +, позиция 63 — /.
URL-безопасный вариант (RFC 4648 §5, также известный как base64url) меняет + на - и / на _, так что результат можно без дополнительных ухищрений помещать в URL и имена файлов. Оба варианта декодируются одинаково, если применять соответствующий алфавит.
// Standard Base64 alphabet (index → character)
// 0–25: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
// 26–51: a b c d e f g h i j k l m n o p q r s t u v w x y z
// 52–61: 0 1 2 3 4 5 6 7 8 9
// 62: + (or - in URL-safe)
// 63: / (or _ in URL-safe)
// pad: =
// ОТКУДА БЕРЁТСЯ ПАДДИНГ =
Алгоритм ожидает, что длина входных данных кратна 3 байтам (чтобы они ровно разбивались на 4 символа Base64). Когда длина входа не кратна 3, остаётся один или два байта, которых недостаточно, чтобы заполнить последнюю 4-символьную группу вывода. Паддинг символом = показывает, сколько байт не хватило.
• Длина входа mod 3 == 0 → паддинга нет (например, 'Man' → 'TWFu').
• Длина входа mod 3 == 1 → два символа паддинга (например, 'M' → 'TQ==').
• Длина входа mod 3 == 2 → один символ паддинга (например, 'Ma' → 'TWE=').
// Why 'M' becomes 'TQ==':
// Byte: M = 01001101 (8 bits)
// Padded to 12 bits with zeros: 010011 010000
// Decimal: 19, 16 → 'T', 'Q'
// Output length must be multiple of 4 → add '==' padding
// Result: 'TQ=='
//
// When decoding, the decoder strips '==' and recovers the first 8 bits,
// discarding the trailing zero bits.
// ТОЧНО ЛИ НАКЛАДНЫЕ РАСХОДЫ 33%?
Почти, но не совсем. Точная формула — ceil(n / 3) × 4 символов для входа длиной n байт. Для 100 КБ это ceil(102400 / 3) × 4 = 136 534 символа — на 33,3% больше, чем 100 КБ. На практике, как только добавляется gzip или Brotli на уровне HTTP, сетевая стоимость оказывается ближе к 10–15%, потому что текст Base64 сжимается достаточно хорошо (хотя и не так хорошо, как сырой бинарь).
Для маленьких данных паддинг играет заметную роль: кодирование 1 байта занимает 4 символа (в четыре раза больше), кодирование 2 байт тоже занимает 4 символа. Поэтому совсем короткие Base64-строки выглядят удивительно длинными на фоне исходных данных.
// ГДЕ BASE64 ПРИМЕНЯЕТСЯ В РЕАЛЬНОМ МИРЕ
-
>
Data URI в HTML/CSS — встраивание иконок и логотипов прямо в разметку:
<img src="data:image/png;base64,…"> - > Полезные нагрузки JSON и GraphQL — API, которым нужно передавать бинарь (загрузки файлов, превью) внутри текстового протокола
- > Токены JWT — три части JWT, разделённые точками, являются строками base64url
- > HTTP Basic Auth — пара username:password кодируется Base64 в заголовке Authorization (всё равно небезопасно без TLS!)
- > Электронная почта (MIME) — вложения кодируются в Base64, чтобы пережить устаревшие 7-битные SMTP-серверы
- > Сертификаты и ключи формата PEM — блок -----BEGIN CERTIFICATE----- оборачивает DER-данные, закодированные в Base64
- > SSH-ключи — строки в id_rsa.pub и authorized_keys — это публичные ключи в Base64
- > TEXT-колонки базы данных — когда тип BLOB недоступен, Base64 позволяет хранить бинарь в виде текста
- > Переменные окружения — значения Kubernetes Secrets закодированы в Base64 (но НЕ зашифрованы)
- > QR-коды и магические ссылки — короткие Base64-токены, безопасные для встраивания в URL
// BASE64 — ЭТО НЕ ШИФРОВАНИЕ
Это самое распространённое заблуждение. Base64 не скрывает содержимое ваших данных — он всего лишь переписывает бинарь в текст по открытому, детально задокументированному отображению. Пароль, закодированный как cGFzc3dvcmQ=, ровно так же уязвим, как и открытый текст password; любой раскодирует его одним вызовом функции. Относитесь к Base64 так же, как к шестнадцатеричному представлению: это форма записи, а не защита.
Если вам нужна приватность, сначала примените к открытому тексту реальный криптографический примитив (AES-GCM, ChaCha20-Poly1305, age, PGP), а потом закодируйте полученный шифротекст в Base64, если его нужно провести через текстовый канал. Шаг Base64 — это последняя миля, а не уровень безопасности.
Kubernetes Secrets — классическая ловушка: Kubernetes хранит значения секретов в Base64, из-за чего они выглядят как бы защищёнными. Но это не так — любой, у кого есть доступ на чтение в неймспейс, развернёт их обратно. Настоящая защита секретов требует инструментов вроде SealedSecrets, Vault, SOPS или облачных интеграций с KMS.
// БЫСТРОЕ КОДИРОВАНИЕ / ДЕКОДИРОВАНИЕ В КАЖДОМ ЯЗЫКЕ
// JavaScript (browser):
// btoa('Hello') → 'SGVsbG8='
// atob('SGVsbG8=') → 'Hello'
// (btoa/atob only handle Latin-1; use TextEncoder for Unicode)
//
// Node.js:
// Buffer.from('Hello').toString('base64') → 'SGVsbG8='
// Buffer.from('SGVsbG8=', 'base64').toString() → 'Hello'
//
// Python:
// import base64
// base64.b64encode(b'Hello') → b'SGVsbG8='
// base64.b64decode('SGVsbG8=') → b'Hello'
//
// Go:
// base64.StdEncoding.EncodeToString([]byte("Hello")) → "SGVsbG8="
//
// Ruby:
// Base64.strict_encode64('Hello') → 'SGVsbG8='
//
// Shell:
// echo -n 'Hello' | base64 → 'SGVsbG8='
// echo 'SGVsbG8=' | base64 -d → 'Hello'
// ЧАСТЫЕ ОШИБКИ, КОТОРЫХ СТОИТ ИЗБЕГАТЬ
-
>
Кодирование URL-безопасной строки стандартным алфавитом — декодер на принимающей стороне отвергнет
+и/. Используйте base64url, если результат попадёт в URL, cookie или имя файла. - > Двойное кодирование — повторный прогон уже закодированного текста через кодировщик. Всегда проверяйте, не являются ли данные уже Base64.
-
>
Забыли про UTF-8 —
btoa('héllo')падает в браузере, потому чтоéнаходится за пределами Latin-1. Сначала преобразуйте строку в байты черезTextEncoder. -
>
Несовпадения по паддингу — base64url часто отбрасывает паддинг
=. Если ваш декодер строгий, добавьте паддинг обратно:input + '==='.slice((input.length + 3) % 4). - > Предполагать, что Base64 безопасен — это не так. Шифрование — отдельный уровень. Всегда.
- > Использовать Base64 для огромных бинарей — кодирование файла в 500 МБ в одну строку исчерпает память. Используйте потоковую обработку.
-
>
Перенос строк или его отсутствие — MIME/PEM разбивают Base64 на 64 или 76 колонок символом
\n. В большинстве других контекстов ожидается одна строка. Вставляйте или удаляйте переводы строк по ситуации.
// BASE64 ЗА 30 СЕКУНД — ШПАРГАЛКА
- > 64 печатных ASCII-символа (A–Z, a–z, 0–9, +, /) плюс паддинг =
- > 3 байта на входе → 4 символа на выходе (фиксированное соотношение)
- > Результат примерно на 33% длиннее входа
- > Обратим: это кодирование, а не шифрование
- > Используйте base64url (заменяете + → -, / → _) для URL и имён файлов
- > Дополняйте короткий вход 1 или 2 символами =, чтобы длина вывода была кратна 4
- > Для Unicode-текста: сначала закодируйте строку в байты UTF-8, затем примените Base64
- > Декодируется стандартной библиотекой любого популярного языка
- > Безопасен в HTML, JSON, URL, письмах и PEM-файлах
- > Небезопасен как слой защиты — всегда комбинируйте с настоящим шифрованием для чувствительных данных
// СЛЕДУЮЩИЕ ШАГИ
Теперь, когда вы знаете механику, попробуйте наш кодировщик Base64 или декодер Base64 и разберите вывод символ за символом. Для URL-безопасных данных переключайтесь на base64url. Для изображений см. изображение → Base64.
Связанные подробные материалы:
• Base64 URL-safe и стандартный — когда какой использовать
• Base64 для UTF-8 и Unicode — как не попасться в ловушку btoa()
• Base64 в JavaScript и Node.js — atob, btoa, Buffer
• Base64, Base64URL и Base32 в сравнении