Русский

Инструменты разработчика · Кодер и декодер Base64

Объяснение заполнения Base64: что означают знаки = и когда они необходимы

· Как это работает

base64 кодирование рабочий процесс разработчика

Вывод Base64, показывающий блоки заполнения и знаки равенства
Оригинальная векторная иллюстрация ToolAcre

Знак = в конце строки Base64 не является украшением: он записывает, на сколько байт последняя группа была короткой. В этом посте объясняется арифметика, почему в некоторых строках ее нет и почему декодеры расходятся во мнениях по поводу отсутствия заполнения.

Исключение «неправильного заполнения» из токена, который выглядел нормально — неудачное декодирование и один или два пропущенных символа позади него.

Когда декодер Base64 сообщает о неправильном заполнении, строка выглядит полной, но содержит структурную ошибку. Знаки равенства не являются косметическими: каждый кодирует, на сколько байтов была коротка последняя группа, что позволяет декодеру точно знать, когда закончились реальные данные. Понимание этих знаков = и того, почему строгие декодеры отвергают строки без них, превращает загадочную ошибку в предсказуемую арифметику. Сегмент JWT может иметь один =, ни одного или два. Ответ API может закончиться правильно, без заполнения.

Они представляют собой преднамеренный выбор, а не варианты реализации. Процесс декодирования не требует механического заполнения. Заполнение существует для того, чтобы сделать вывод однозначным: имея только строку Base64 без метаданных о длине, декодер считывает заполнение и точно знает, где закончились данные. Base64 кодирует группы по три байта в четыре символа. Три байта — это 24 bits, которые прекрасно перегруппируются в четыре 6-битных индекса; каждый выбирает один из символов 64 Base64. Когда входные данные не кратны трем, кодер сталкивается с остатками: один или два байта не могут делиться на три без остатка.

Группы по три байта, блоки по четыре символа — почему длина ввода по модулю 3 решает, появятся ли знаки = ноль, один или два

Кодер дополняет эти группы, сдвигая биты в первые индексы, оставляя конечные значения нулевыми. Чтобы отметить это намеренно, он добавляет знаки =: ноль для полных групп, один для двухбайтовых финалов, два для однобайтовых финалов. Арифметика детерминированная: зная длину входных данных в байтах, вы можете немедленно вычислить заполнение. Один байт создает два символа Base64 плюс два =. Два байта образуют три символа плюс один =. Три байта дают четыре без заполнения.

Любые входные данные, не кратные трем байтам, будут иметь заполнение; любое кратное число не будет. Это не выбор — это арифметика. Строка без заполнения должна представлять собой три байта. Строка с одним равным должна представлять два. Заполнение кодирует входную длину по модулю три. Исследуйте преобразование трех входных данных: одиночного a, пары ab, тройного abc. ASCII a — байт 0x61; Base64 кодирует его как 0x61 00 00, перегруппировываясь в шестибитные группы.

Что содержат биты заполнения и почему их проверяет строгий декодер — какие биты должны быть равны нулю и что означает каноническое кодирование

Индексы 24, 4, 0, 0 соответствуют Y, E, A, A. Поскольку две группы были заполнены, кодер добавляет два знака =, создавая YQ==. Для ab байты 0x61 0x62 становятся 0x61 0x62 00. Bits перегруппируются в индексы 24, 22, 8, 0, выведите YWI=. Для abc байты перегруппируются в индексы 24, 22, 9, 35, выведите YWJj без заполнения. Заполнение не является произвольным: оно выпадает из разрядности. Когда вы декодируете строку Base64, декодер считывает каждый символ, ищет его шестибитный индекс, упаковывает биты в байты.

Для YQ== символы Y, E, A, A распаковываются в биты. Перегруппировка в восьмибитные байты дает один байт 0x61. Декодер отбрасывает биты заполнения (конечные нули) и сообщает один байт. Строгий декодер проверяет, что биты заполнения на самом деле равны нулю; в противном случае ввод не был каноническим, а это означает, что кто-то закодировал с использованием другого битового расположения, и декодирование неоднозначно. Системы, полностью опускающие отступы, идут на преднамеренные компромиссы. Сегменты JWT используют Base64url без заполнения, полагаясь на то, что потребители знают ожидаемую длину вывода или предполагают ее.

Рабочий пример: кодирование «a», «ab» и «abc» вручную — три входа, три результата заполнения, показаны побитно.

RFC 4648 разрешает отсутствие заполнения, но указывает декодерам принять его, если оно присутствует. Библиотеки кода различаются: некоторые восстанавливают отсутствующие дополнения и продолжают работу; другие потерпят неудачу. Когда вы сталкиваетесь с токенами, которые не декодируются, добавление нужного количества знаков = часто исправляет ситуацию. Обязательные = знаки всегда равны нулю, одному или двум, в зависимости от длины строки по модулю четыре. Если длина строки Base64 не кратна четырем, заполнение определенно отсутствует или повреждено.

Длина 5 не может быть допустимой в Base64: каждый полный символ кодирует шесть бит, поэтому четыре символа кодируют 24 bits (три байта), а пять кодируют 30 bits, который не кратен восьми и не может стать байтом. Декодер должен либо отклонить это, либо добавить дополнение. Если длина равна 2 по модулю 4, добавьте два =. Если 3 по модулю 4, добавьте один =. Если 0 по модулю 4, ничего не добавляйте. В строке длиной 3 отсутствует необходимое =; добавьте его, и он станет действительным перед декодированием.

Почему некоторые системы полностью отказываются от заполнения — сегменты JWT и URL токены, в которых отсутствует знак =, и как восстановить его по длине

Объединение двух дополненных строк Base64 прерывается, если заполнение остается на месте. Два отдельных кодирования, соединенные напрямую, создают случайные символы заполнения, которые нарушают алфавит декодирования. Вот почему некоторые системы перед объединением удаляют заполнение: токен, состоящий из трех сегментов Base64url, соединенных точками, не имеет заполнения внутри сегментов, что упрощает объединение. При построении значения Base64 из частей проверьте, дополняется ли каждая часть и удаляется или последовательно добавляется заполнение перед любыми операциями.

Кодер и декодер Base64 применяет RFC 4648, который по умолчанию требует заполнения. Когда вы вводите текст и запрашиваете вывод в формате Base64, инструмент выдает дополненный результат: каноническую форму. Если вы видите Base64 без заполнения и хотите его декодировать, проверьте, принимает ли ваш декодер отсутствующее дополнение. Инструмент принимает как дополненные, так и недополненные входные данные и правильно восстанавливает исходные байты. При отладке подсчет длины по модулю четыре показывает, было ли удалено заполнение, а формула показывает, какое заполнение должно присутствовать.

Распространенные ошибки: обрезка = как если бы это были пробелы или объединение двух дополненных строк — как каждая из них искажает декодирование.

Base32 и Base16 (шестнадцатеричный) имеют разные правила заполнения, определенные в разделах RFC 4648 6 и 7. Base32 использует =, но конечная группа может состоять из символов 2, 4, 5, 7 или 8 в зависимости от входной длины по модулю пять. Шестнадцатеричное число не требует заполнения; он всегда отображает один байт в два символа без остатка. MIME Обертка Base64 касается заполнения: строка, заключенная в столбец 76, все еще имеет заполнение в самом конце, всего несколькими строками позже.

Понимание заполнения для Base64 связано с пониманием расположения битов и входной длины по модулю три; как только вы видите арифметику, заполнение становится прямым следствием, а не правилом, которое нужно запомнить. Заполнение является производным, а не волшебным.

Что здесь не распространяется — правила заполнения base32 и base16, а также соглашения о длине строк MIME.

Учитывая строку Base64 любой длины, вы можете восстановить каноническую дополненную форму, разделив количество символов на четыре, взяв остаток и добавив соответствующее количество знаков =. Вот почему отсутствие = можно исправить и почему строгие декодеры могут быть прощающими: заполнение несет информацию (в какую ветвь из трех случаев попал ваш ввод), но эта информация может быть вычислена только на основе длины.

Кодер и декодер Base64 немедленно отображает дополненный вывод, поэтому вы можете сравнить декодированные байты с исходным текстом и проверить работу туда и обратно. Base64 и связанные с ним кодировки расширяют принцип перегруппировки битов в символы разной ширины. RFC 4648 определяет все три, и понимание одного делает концептуально простыми другие. Ключевой вывод заключается в том, что кодирование — это чистая манипуляция битами: выберите размер алфавита, соответствующим образом сгруппируйте биты, найдите каждую группу в таблице.

Вывод: заполнение можно вывести, поэтому недостающее = можно исправить — как кодировщик и декодер Base64 отображает дополненную каноническую форму любого текста, который вы кодируете.

Декодирование происходит в обратном порядке: поиск каждого символа, извлечение битов, их перегруппировка, запись байтов. Именно это детерминированное двустороннее сопоставление является причиной того, что Base64 надежно работает на всех платформах и языках. Ошибки в кодировании и декодировании часто возникают из-за неправильного понимания различий в заполнении или алфавите. Если декодирование завершается неудачно из-за ошибок заполнения, проверьте, ожидает ли декодер канонический Base64 (строго дополненный) или принимает варианты. Если это не удается из-за ошибок символов, проверьте, является ли входной URL-адрес base64url и декодер ожидает стандартный Base64.

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