Коротко
Base64 позволяет записать любую последовательность байтов 64 безопасными символами (A–Z, a–z, 0–9, +, /). Он нужен, чтобы двоичные данные (картинка, PDF, ключ) могли пройти через системы, которые понимают только текст: тело письма, строки JSON, URL, атрибуты HTML. Это кодирование, а не шифрование: ключа нет, а декодирование выполняется одним вызовом функции, доступной в любом языке и в консоли разработчика любого браузера. Пароль, сохранённый «в Base64», хранится открытым текстом, просто с лишними шагами.
Зачем он появился
Электронную почту в 1970-х проектировали под 7-битный ASCII. Когда появились вложения (MIME, 1992), байты JPEG пришлось маскировать под безобидный текст: они используют все 256 значений, включая те, что почтовый сервер понимает как «конец строки» или «конец письма». Ответом стал Base64: берутся три байта (24 бита), делятся на четыре группы по 6 бит, и каждая группа заменяется одним из 64 символов. Каждые 3 байта превращаются в 4 символа. Отсюда знаменитые 33 % накладных расходов: вложение на 3 МБ даёт письмо на 4 МБ, и поэтому в лимит почты «25 МБ» помещается файл на 18 МБ.
Тот же приём встречается везде, где единственный канал текстовый: небольшая картинка прямо внутри веб-страницы (data:image/png;base64,…), двоичный токен в HTTP-заголовке (Authorization: Basic …), сертификаты и ключи в файлах PEM, массив байтов в поле JSON.
Варианты, на которых спотыкаются
- Стандартный и URL-safe. Стандартный Base64 использует
+и/, а оба символа в URL имеют особый смысл. Алфавит URL-safe (RFC 4648 §5) заменяет их на-и_. Токен, закодированный одним вариантом и декодируемый другим, ломается именно на этих двух символах. JWT используют URL-safe. - Выравнивание. Длина результата всегда кратна четырём и дополняется знаками
=. Некоторые генераторы отбрасывают выравнивание (опять JWT), а некоторые декодеры его требуют. Если корректная в остальном строка не декодируется, добавляйте=, пока длина не станет кратной четырём. - Перенос строк. MIME переносит строки через 76 символов, PEM через 64. Декодеры обычно игнорируют пробельные символы, но не все.
- Кодировка текста под ним. Base64 кодирует байты. Чтобы закодировать строку, её сначала переводят в байты, и выбор между UTF-8, UTF-16 и Latin-1 меняет результат. Классическая ошибка JavaScript, когда
btoa("é")выбрасывает исключение, возникает именно поэтому:btoaпринимает только Latin-1. Сначала получите байты UTF-8.
Base64: кодирование поддерживает оба алфавита, выравнивание, перенос строк и UTF-8 и декодирует как в текст, так и в файл для скачивания, поэтому data: URL или вложение из письма можно превратить обратно в исходный файл прямо в браузере.
Кодирование, хеширование, шифрование
В обиходных текстах эти три слова путают, хотя означают они совершенно разные вещи.
| Обратимо? | Нужен ключ? | Назначение | |
|---|---|---|---|
| Кодирование (Base64, hex, URL-кодирование) | Да, кем угодно | Нет | Подогнать данные под канал |
| Хеширование (SHA-256, MD5) | Нет | Нет | Получить отпечаток данных, проверить, что они не изменились |
| Шифрование (AES, RSA) | Да, с ключом | Да | Скрыть данные от всех, у кого нет ключа |
Хеш файла позволяет проверить, что скачанное совпадает с тем, что выложил издатель. Хеши (MD5, SHA-256) считает MD5, SHA-1 и SHA-256/384/512 в браузере и сравнивает результат с вставленной контрольной суммой. Ничего при этом не скрывается: файл остаётся тем же файлом. Секретность из трёх вариантов даёт только шифрование, и оно надёжно ровно настолько, насколько надёжен ключ. Здесь и пригодится действительно случайный пароль.
Если вы собирались закодировать секрет в Base64
Не надо. В частности:
- Конфигурационные файлы с паролями в Base64 (распространённая практика в старых приложениях на Java и .NET) содержат пароли открытым текстом для любого, кто откроет файл.
- «Замаскированные» ключи API во фронтенд-JavaScript видны на вкладке сети, в Base64 они или нет.
- Basic-аутентификация по обычному HTTP передаёт имя пользователя и пароль в Base64, то есть фактически открытым текстом. Она допустима только внутри HTTPS, где шифрованием занимается транспорт.
Если данные нужно хранить или передавать в секрете, зашифруйте их настоящим шифром (AES-256-GCM через криптобиблиотеку платформы), а затем, если они должны пройти по текстовому каналу, закодируйте шифротекст в Base64. Кодировать после шифрования нормально. Ошибка в том, чтобы кодировать вместо шифрования.