Кодирование Base64: полное руководство для разработчика
Кодирование Base64 встречается практически в каждой области веб-разработки — токены JWT, URI данных, вложения электронной почты, полезные нагрузки API, криптографические подписи и конфигурационные файлы. Несмотря на повсеместность, оно часто понимается неправильно: разработчики порой путают его с шифрованием или применяют в ситуациях, где это лишь ухудшает положение. В этом руководстве объясняется, как именно работает Base64, когда его следует использовать, когда избегать и как правильно применять в JavaScript.
Что такое Base64 (и чем не является)
Base64 — схема кодирования бинарных данных в текст. Её единственная задача — преобразовать произвольные бинарные данные в строку печатаемых ASCII-символов. Оно не сжимает данные, не шифрует и не проверяет их. Это чисто преобразование представления — байты, которые могут включать нулевые байты, управляющие символы или значения, нарушающие работу текстовых протоколов, преобразуются в безопасный набор символов.
Название происходит от 64 печатаемых символов, используемых в качестве алфавита кодирования: A–Z (26 символов), a–z (26 символов), 0–9 (10 символов), плюс + и / (2 символа). Символ = используется для дополнения. Поскольку для каждой позиции символа возможны 64 значения и 2^6 = 64, каждый символ Base64 кодирует ровно 6 бит исходных данных.
Как работает Base64 — пошаговый разбор «Hello»
Лучший способ понять Base64 — проследить конкретный пример. Закодируем строку Hello.
Шаг 1: преобразование в байты
Каждый символ в Hello соответствует своему ASCII-коду, который затем выражается в бинарном виде (8 бит на байт):
H e l l o
72 65 6C 6C 6F (ASCII / hex)
01001000 01100101 01101100 01101100 01101111 (бинарный) Шаг 2: группировка в 6-битные фрагменты
Соединяем все биты: 0100100001100101011011000110110001101111 (40 бит). Base64 обрабатывает по 3 байта (24 бита) за раз и отображает их на 4 символа Base64 (4 × 6 = 24 бита). Группируем 40 бит в 6-битные фрагменты, дополняя последнюю группу нулями:
010010 000110 010101 101100 011011 000110 1111
18 6 21 44 27 6 (последняя группа дополнена до 6 бит: 111100 = 60) Шаг 3: отображение в алфавит Base64
Каждое 6-битное значение отображается на символ в алфавите Base64 (A=0, B=1, ... Z=25, a=26, ... z=51, 0=52, ... 9=61, +=62, /=63). Поскольку 5 байт не кратно 3, добавляется один символ дополнения =:
Индекс: 18 6 21 44 27 6 60
Символ: S G V s b G 8
Результат: SGVsbG8= (= — символ дополнения) Результат
"Hello" → "SGVsbG8="
Это можно мгновенно проверить с помощью кодировщика/декодировщика Base64 Toova — вставьте Hello, нажмите «Кодировать» и получите SGVsbG8=. Нажмите «Декодировать» для обратного преобразования.
Накладные расходы 33% на размер
Кодирование Base64 всегда увеличивает размер данных примерно на 33%. Математика проста: 3 байта ввода (24 бита) дают 4 символа Base64 (24 бита, закодированных в 4 × 6-битных группах). Это соотношение 4/3, или 33,3% накладных расходов. С учётом символов дополнения реальные накладные расходы составляют от 33% до 36% в зависимости от длины ввода.
Это существенно влияет на производительность. Изображение размером 1 МБ, встроенное как data URI в Base64 в HTML, становится примерно 1,37 МБ. API, кодирующий все бинарные полезные нагрузки в Base64, отправляет на 33% больше данных, чем необходимо. Для небольших значений, таких как короткие токены или контрольные суммы, накладные расходы незначительны. Для больших файлов это реальная стоимость.
URL-безопасный вариант
Стандартный Base64 использует + и / в качестве последних двух символов алфавита. Оба символа проблематичны в URL:
+декодируется как пробел в строках запроса/является разделителем пути в URL
URL-безопасный Base64 (также называемый Base64url, определённый в RFC 4648, раздел 5) заменяет + на - и / на _. Дополнение (=) обычно опускается в URL-безопасных контекстах, поскольку некоторые парсеры URL также могут его неправильно интерпретировать.
Токены JWT используют Base64url без дополнения. При ручном декодировании заголовка или полезной нагрузки JWT необходимо обрабатывать как замену символов, так и отсутствующее дополнение. Вот как это сделать в JavaScript:
// URL-безопасный Base64 (заменяем + на - и / на _)
function toBase64Url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function fromBase64Url(base64url) {
const padded = base64url.padEnd(
base64url.length + (4 - base64url.length % 4) % 4,
'='
);
return atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
} Кодировщик/декодировщик Base64 Toova поддерживает стандартный и URL-безопасный варианты с помощью одного переключателя.
Base64 в JavaScript
Браузер: btoa() и atob()
Браузеры предоставляют две встроенные функции: btoa() (binary to ASCII — кодирование) и atob() (ASCII to binary — декодирование). Несмотря на запутанный порядок названий, они доступны в браузерах уже более десяти лет.
// Браузер — atob / btoa (только строки, ASCII-совместимые)
const encoded = btoa("Hello");
console.log(encoded); // "SGVsbG8="
const decoded = atob("SGVsbG8=");
console.log(decoded); // "Hello"
Важное ограничение: btoa() принимает только строки с символами в диапазоне Latin-1 (кодовые точки 0–255). Если передать строку с символами Unicode, например эмодзи или символами CJK, будет выброшено DOMException. Для кодирования произвольных бинарных данных сначала преобразуйте их в Uint8Array:
// Браузер — кодирование произвольных бинарных данных (Uint8Array)
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const encoded = btoa(String.fromCharCode(...bytes));
console.log(encoded); // "SGVsbG8="
Для кодирования произвольных строк, которые могут содержать символы Unicode, рекомендуемый современный подход — использовать TextEncoder для получения Uint8Array, а затем кодировать как показано выше.
Node.js: Buffer
Node.js предоставляет класс Buffer, который корректно обрабатывает бинарные данные и поддерживает несколько кодировок, включая Base64 и Base64url:
// Node.js — Buffer (корректно обрабатывает бинарные данные)
const encoded = Buffer.from('Hello').toString('base64');
console.log(encoded); // "SGVsbG8="
const decoded = Buffer.from('SGVsbG8=', 'base64').toString('utf8');
console.log(decoded); // "Hello"
// URL-безопасный вариант в Node.js
const urlSafe = Buffer.from('Hello').toString('base64url');
console.log(urlSafe); // "SGVsbG8" (без дополнения)
Опция кодирования base64url (доступна с Node.js 16) автоматически обрабатывает замену символов и удаление дополнения, что значительно удобнее ручного преобразования.
Для браузерных сред, которым нужно обрабатывать большие бинарные файлы, метод FileReader.readAsDataURL кодирует файл как data URI в Base64, не загружая всё в память сразу.
Когда использовать Base64
Встраивание бинарных данных в текстовые протоколы
Исходное назначение Base64 — кодирование бинарных вложений электронной почты в SMTP, протоколе, поддерживающем только 7-битный ASCII-текст. Тот же принцип применяется везде, где нужно включить бинарные данные в формат, не поддерживающий сырые байты: полезные нагрузки JSON API, XML-документы, HTML-атрибуты, значения CSS, HTTP-заголовки.
URI данных для небольших ресурсов
CSS и HTML позволяют встраивать изображения, шрифты и SVG в виде data URI в Base64. Это устраняет один HTTP-запрос для небольших ресурсов, таких как иконки, и устраняет мерцание неотображённого контента для критических изображений выше сгиба.
<!-- Встроенная SVG-иконка как Base64 data URI -->
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c..." alt="иконка" />
<!-- Встроенный CSS-фон -->
.icon {
background-image: url("data:image/png;base64,iVBORw0KGgo...");
} Компромисс: data URI в Base64 не могут кешироваться отдельно от HTML/CSS-файла, в котором они содержатся. Если изображение никогда не меняется, а окружающий HTML меняется, браузер перезагружает данные изображения при каждой перезагрузке страницы. Используйте data URI только для небольших ресурсов (желательно менее 4 КБ), где устранение одного запроса перевешивает потери от кеширования.
Кодирование бинарных данных для JSON или URL-параметров
JSON — текстовый формат, он не может непосредственно представлять сырые бинарные байты. Когда API нужно передать бинарные данные (миниатюры изображений, криптографические подписи, сжатые данные), Base64 является стандартным способом включить их в JSON-полезную нагрузку. Аналогично, если нужно передать бинарные данные в параметре URL-запроса, кодирование Base64url гарантирует, что данные не будут повреждены при процентном кодировании.
JWT и другие форматы токенов
Токены JWT используют Base64url для кодирования разделов заголовка и полезной нагрузки. Это делает токен печатаемой, URL-безопасной строкой, которую можно передавать в HTTP-заголовках, cookie или URL-параметрах. Кодирование нужно не для безопасности (полезная нагрузка читаема для любого, у кого есть токен) — оно нужно исключительно для безопасной передачи.
Когда НЕ использовать Base64
Безопасность и конфиденциальность
Base64 не обеспечивает никакой защиты. Оно тривиально обратимо за миллисекунды. Не используйте его для «обфускации» паролей, ключей API или чувствительных значений конфигурации. Любой разработчик, увидевший строку Base64, немедленно её декодирует. Для конфиденциальности используйте шифрование.
Хранение паролей
Хранение паролей в кодировке Base64 равнозначно хранению их в открытом виде — кодирование мгновенно обратимо. Пароли должны быть хешированы с помощью специальной функции хеширования паролей, такой как bcrypt, Argon2 или scrypt.
Большие бинарные файлы
Кодирование файла размером 10 МБ в Base64 даёт строку 13,7 МБ. Если хранить это в столбце базы данных, выполнять поиск или передавать по API, приходится платить 33% накладных расходов каждый раз. Для больших бинарных данных используйте специализированное бинарное хранилище: столбцы BLOB/BYTEA базы данных, объектное хранилище вроде S3 или GCS, или передавайте бинарные данные потоком напрямую.
Ситуации, когда можно использовать бинарные данные напрямую
Если протокол или формат поддерживает сырые бинарные данные — например, WebSocket с бинарным типом сообщений, HTTP-загрузка multipart/form-data или бинарный формат файла — используйте бинарные данные напрямую. Base64 необходим только тогда, когда транспортная среда действительно не может обрабатывать сырые байты.
Распространённые ошибки
Путаница кодирования с шифрованием
Это наиболее распространённая ошибка. Base64 виден. Это не механизм безопасности. Комментарии в коде вроде «пароль хранится в Base64 для безопасности» указывают на серьёзное непонимание, которое должно выявляться при код-ревью.
Использование btoa() со строками Unicode
Вызов btoa() со строкой, содержащей символы с кодовыми точками выше 255, выбрасывает DOMException: Failed to execute 'btoa': The string to be encoded contains characters outside of the Latin1 range. Всегда преобразуйте строку в Uint8Array через TextEncoder, прежде чем кодировать строки, которые могут содержать символы Unicode.
Забытое дополнение при декодировании
Длина строк Base64 должна быть кратна 4. Если строка Base64 сгенерирована без дополнения (часто в URL-безопасном кодировании), необходимо добавить правильное количество символов = перед декодированием. Строка Base64 длиной n требует (4 - n % 4) % 4 символов дополнения. Забыть об этом — значит получить ошибки декодирования, которые трудно диагностировать.
Двойное кодирование
Строка Base64 сама по себе является корректным ASCII, поэтому btoa(btoa(data)) работает без ошибок, но даёт двукратно закодированный вывод. При передаче значений Base64 через несколько уровней сериализации (JSON внутри JSON, например) легко закодировать одни и те же данные дважды. Всегда декодируйте ровно столько раз, сколько кодировали.
Краткий справочник: Base64 на практике
Для кодирования и декодирования в браузере без написания кода кодировщик/декодировщик Base64 Toova работает полностью в браузере — без обращений к серверу. Поддерживает стандартный и URL-безопасный варианты, загрузку файлов для кодирования бинарных файлов, а также текстовый и шестнадцатеричный вывод для декодированных данных.
Если вы работаете с закодированным содержимым внутри URL, кодировщик/декодировщик URL обрабатывает процентное кодирование отдельно от Base64. Для HTML-сущностей конвертер HTML-сущностей обрабатывает экранирование символов в HTML-контекстах. Это разные схемы кодирования — каждая предназначена для конкретного случая использования.
Канонический стандарт для Base64 — RFC 4648, определяющий стандартный Base64 (раздел 4), Base64url (раздел 5) и Base32 (разделы 6–7). Для браузерных API btoa() и atob() документация MDN по btoa() подробно освещает совместимость с браузерами и ограничения для Unicode.
Резюме
Кодирование Base64 преобразует бинарные данные в печатаемый ASCII с использованием алфавита из 64 символов. Оно увеличивает размер данных на 33%, полностью обратимо и не обеспечивает никакой защиты. Используйте его, когда нужно встраивать бинарные данные в текстовый формат — JSON-полезные нагрузки, HTML data URI, токены JWT, вложения электронной почты, URL-параметры. Избегайте его, когда нужна безопасность, когда транспорт поддерживает бинарные данные напрямую, или когда 33% накладные расходы важны при масштабировании.
Понимание того, чем является Base64, а чем нет, позволяет избежать наиболее распространённых ошибок: использования для обеспечения безопасности, излишнего применения к большим файлам и путаницы с другими схемами кодирования, такими как URL-кодирование или HTML-сущности. Каждая схема кодирования решает конкретную задачу. Base64 решает ровно одну: делает бинарные данные безопасными для текстовых каналов.
Готовы кодировать или декодировать? Попробуйте кодировщик/декодировщик Base64 Toova — вставьте текст или перетащите файл, переключитесь между стандартным и URL-безопасным режимом, скопируйте результат. Без аккаунта, без сервера, без ограничений.