Почему мы используем Base64?

276

Википедия говорит

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

Но разве это не то, что данные всегда хранятся / передаются в двоичном виде, потому что память, которую имеют наши машины, хранит двоичные данные, и это просто зависит от того, как вы их интерпретируете? Таким образом, независимо от того, кодируете ли вы битовую комбинацию 010011010110000101101110как Manв ASCII или как TWFuв Base64, вы в конечном итоге собираетесь сохранить ту же битовую комбинацию.

Если конечное кодирование выражается в единицах нулей и единиц, и каждая машина и носитель могут с ними справиться, как это имеет значение, если данные представлены в виде ASCII или Base64?

Что означает «средства массовой информации, предназначенные для работы с текстовыми данными»? Они могут иметь дело с бинарным => они могут иметь дело с чем угодно.


Спасибо всем, я думаю, что теперь понимаю.

Когда мы отправляем данные, мы не можем быть уверены, что данные будут интерпретированы в том же формате, который мы намеревались. Итак, мы отправляем данные, закодированные в каком-то формате (например, Base64), который понимают обе стороны. Таким образом, даже если отправитель и получатель по-разному интерпретируют одни и те же вещи, но поскольку они согласовывают кодированный формат, данные не будут интерпретироваться неправильно.

Из примера Марка Байерса

Если я хочу отправить

Hello
world!

Один из способов - отправить его в формате ASCII

72 101 108 108 111 10 119 111 114 108 100 33

Но байт 10 может быть неправильно интерпретирован как перевод строки на другом конце. Итак, мы используем подмножество ASCII для его кодирования следующим образом

83 71 86 115 98 71 56 115 67 110 100 118 99 109 120 107 73 61 61

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

Lazer
источник
6
Историческая справка: почтовые серверы раньше были 7-битными ASCII. Многие из них устанавливают старший бит на 0, поэтому вам нужно было отправлять только 7-битные значения. См. En.wikipedia.org/wiki/Email#Content_encoding
Гарольд Л
53
Мы используем base64, потому что он более читабелен, чем Perl
Martin
2
@ Мартин, ты шутишь. Perl трудно читать, но base64 вообще не читается.
Питер Лонг
1
@Lazer Ваше изображение отсутствует
Мик
2
@Lazer, «Но байт 10 может быть неправильно интерпретирован как перевод строки на другом конце». Зачем? обе стороны договорились о ASCII, и они должны толковать это правильно!
ProgramCpp

Ответы:

299

Ваша первая ошибка - думать, что кодировка ASCII и кодировка Base64 взаимозаменяемы. Они не. Они используются для разных целей.

  • Когда вы кодируете текст в ASCII, вы начинаете с текстовой строки и конвертируете ее в последовательность байтов.
  • Когда вы кодируете данные в Base64, вы начинаете с последовательности байтов и преобразуете ее в текстовую строку.

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


Компьютеры общаются в двоичном формате - 0 и 1 - но люди обычно хотят общаться с более богатыми данными, такими как текст или изображения. Для передачи этих данных между компьютерами их сначала нужно кодировать в 0 и 1, отправлять, а затем снова декодировать. Чтобы взять текст в качестве примера - есть много разных способов выполнить эту кодировку. Было бы намного проще, если бы мы все могли договориться об одной кодировке, но, к сожалению, это не так.

Первоначально было создано много разных кодировок (например, код Бодо ), в которых использовалось разное количество бит на символ, пока в конце концов ASCII не стал стандартом с 7 битами на символ. Однако большинство компьютеров хранят двоичные данные в байтах, состоящих из 8 бит каждый, поэтому ASCII не подходит для передачи данных этого типа. Некоторые системы даже стирают самый значительный бит. Кроме того, разница в кодировках окончания строк в разных системах означает, что символы ASCII 10 и 13 также иногда изменялись.

Для решения этих проблем была введена кодировка Base64 . Это позволяет вам кодировать байты aribtrary в байты, которые, как известно, безопасны для отправки без повреждения (буквенно-цифровые символы ASCII и пара символов). Недостатком является то, что кодирование сообщения с использованием Base64 увеличивает его длину - каждые 3 байта данных кодируются до 4 символов ASCII.

Для надежной отправки текста вы можете сначала кодировать в байты, используя выбранную кодировку текста (например, UTF-8), а затем впоследствии Base64 кодировать полученные двоичные данные в текстовую строку, которую безопасно отправлять в кодировке ASCII. Получатель должен будет отменить этот процесс, чтобы восстановить исходное сообщение. Это, конечно, требует, чтобы получатель знал, какие кодировки использовались, и эту информацию часто нужно отправлять отдельно.

Исторически оно использовалось для кодирования двоичных данных в почтовых сообщениях, где почтовый сервер мог изменять окончания строк. Более современный пример - использование кодировки Base64 для встраивания данных изображения непосредственно в исходный код HTML . Здесь необходимо кодировать данные, чтобы символы, такие как «<» и «>», не интерпретировались как теги.


Вот рабочий пример:

Я хочу отправить текстовое сообщение в две строки:

Привет
Мир!

Если я отправлю его как ASCII (или UTF-8), это будет выглядеть так:

72 101 108 108 111 10 119 111 114 108 100 33

Байт 10 поврежден в некоторых системах, поэтому мы можем закодировать эти байты в формате Base64 в виде строки Base64:

SGVsbG8sCndvcmxkIQ ==

Который при кодировании с использованием ASCII выглядит следующим образом:

83 71 86 115 98 71 56 115 67 110 100 118 99 109 120 107 73 61 61

Все байты здесь являются безопасными байтами, поэтому вероятность того, что любая система испортит это сообщение, очень мала. Я могу отправить это вместо моего исходного сообщения и позволить получателю отменить процесс, чтобы восстановить исходное сообщение.

Марк Байерс
источник
4
«большинство современных протоколов связи не портят данные» - хотя, например, электронная почта могла бы, агент доставки заменил строку символов «\ nFrom» на «\ n> From», когда сохранял сообщение в почтовом ящике. Или HTTP-заголовки заканчиваются символом новой строки и не имеют обратимого способа избежать новых строк в данных (продолжение строки сопоставляет пробел), поэтому вы не можете просто вставить произвольный ASCII в них. Base64 лучше, чем просто 7-битный сейф, он буквенно-цифровой и - = + / безопасный.
Стив Джессоп
1
«Недостатком является то, что кодирование сообщения с использованием Base64 увеличивает его длину - каждые 3 байта данных кодируются до 4 байтов». Как он увеличивается до 4 байтов? Разве это не будет все еще 3 * 8 = только 24 бита?
Лазер
4
@Lazer: нет. Посмотрите на собственный пример - «Человек» - это base-64, закодированный как «TWFu». 3 байта -> 4 байта. Это связано с тем, что входные данные могут быть любыми из 2 ^ 8 = 256 возможных байтов, в то время как выходные данные используют только 2 ^ 6 = 64 из них (и =, чтобы помочь указать длину данных). 8 битов на квартет вывода «теряются», чтобы на выходе не содержались «захватывающие» символы, даже если они есть.
Стив Джессоп
3
Может быть полезно переформулировать «Когда вы кодируете данные в Base64, вы начинаете с последовательности байтов и конвертируете ее в текстовую строку» как «Когда вы кодируете данные в Base64, вы начинаете с последовательности байтов и конвертируете их в последовательность байтов, состоящая только из значений ASCII ". SMTP требует последовательности байтов, состоящей только из символов ASCII, поэтому Base64 (и для печати в кавычках) используются в качестве кодировок передачи содержимого. Отличный обзор!
ALEXintlsos
1
Я бы проголосовал, но имеет 64 голоса. Извините, это идеально.
Джесси Катринк,
61

Кодирование двоичных данных в XML

Предположим, вы хотите встроить пару изображений в документ XML. Изображения являются двоичными данными, а документ XML - текстовым. Но XML не может обрабатывать встроенные двоичные данные. Итак, как вы это делаете?

Одним из вариантов является кодирование изображений в base64, превращение двоичных данных в текст, который может обрабатывать XML.

Вместо того:

<images>
  <image name="Sally">{binary gibberish that breaks XML parsers}</image>
  <image name="Bobby">{binary gibberish that breaks XML parsers}</image>
</images>

вы делаете:

<images>
  <image name="Sally" encoding="base64">j23894uaiAJSD3234kljasjkSD...</image>
  <image name="Bobby" encoding="base64">Ja3k23JKasil3452AsdfjlksKsasKD...</image>
</images>

А синтаксический анализатор XML сможет правильно анализировать документ XML и извлекать данные изображения.

yfeldblum
источник
Это может быть то, как работает старый .mhtформат Microsoft (HTML-файл + изображения в одном файле).
Шридхар Сарнобат
38

Почему бы не взглянуть на RFC, который в настоящее время определяет Base64 ?

Базовое кодирование данных используется во многих ситуациях для хранения или передачи
данных в средах, которые, возможно, по устаревшим причинам, ограничены данными US-ASCII [1]. Базовое кодирование также может использоваться в новых приложениях, которые не имеют устаревших ограничений, просто потому, что это позволяет манипулировать объектами с помощью текстовых редакторов.

В прошлом разные приложения предъявляли разные требования и, следовательно, иногда реализовывали базовые кодировки немного по-разному. Сегодня в спецификациях протокола иногда используются базовые кодировки вообще и «base64» в частности, без точного описания или ссылки. Многоцелевые расширения электронной почты в Интернете (MIME) [4] часто используются в качестве справочного материала для base64 без учета последствий для переноса строк или не алфавитных символов. Целью данной спецификации является установление общего алфавита и соображений кодирования. Надеемся, что это уменьшит неоднозначность в других документах, что приведет к лучшей совместимости.

Изначально Base64 был разработан как способ, позволяющий прикреплять двоичные данные к электронным письмам как часть многоцелевых расширений Интернет-почты.

Билли ОНил
источник
26

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

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

Håvard S
источник
Так что, как и в Base64, в основном и источник, и пункт назначения будут интерпретировать данные одинаково, поскольку, скорее всего, они будут интерпретировать эти 64 символа одинаково, даже если они интерпретируют управляющие символы по-разному. Это правильно?
Лазер
6
Эти данные могут даже быть уничтожены в пути. Например, многие программы FTP переписывают окончания строк с 13,10 до 10 или наоборот, если операционная система сервера и клиента не совпадает, а передача помечается как текстовый режим. FTP - это только первый пример, который мне пришел в голову, он не очень хороший, потому что FTP поддерживает двоичный режим.
Хендрик Браммерманн
@nhnb: я думаю, что FTP - хороший пример, поскольку он показывает, что текстовый режим не подходит для вещей, которые хотят двоичные данные.
Джеймсдлин
Что такое текстовое медиа?
Корай Тугай
18

Более того, носитель проверяет строковое кодирование, поэтому мы хотим убедиться, что данные приемлемы для обрабатывающего приложения (и не содержат двоичной последовательности, представляющей, например, EOL)

Представьте, что вы хотите отправить двоичные данные в электронном письме с кодировкой UTF-8 - электронное письмо может отображаться некорректно, если поток единиц и нулей создает последовательность, которая не является допустимой Unicode в кодировке UTF-8.

То же самое происходит в URL, когда мы хотим кодировать символы, недопустимые для URL в самом URL:

http://www.foo.com/hello мой друг -> http://www.foo.com/hello%20my%20friend

Это потому, что мы хотим отправить пространство над системой, которая будет думать, что пространство вонючее.

Все, что мы делаем, - это обеспечение соответствия 1: 1 между хорошо известной, приемлемой и не наносящей ущерб последовательности битов другой буквальной последовательности битов, и что приложение обработки не различает кодировку.

В вашем примере manможет быть действительным ASCII в первой форме; но часто вам может потребоваться передать значения, которые являются случайными двоичными (например, отправка изображения в электронном письме):

MIME-версия: 1.0
Content-Description: "Base64 encode of a.gif"
Content-Type: image / gif; name = "a.gif"
Content-Transfer-Encoding: Base64
Content-Disposition: вложение; имя файла = "a.gif"

Здесь мы видим, что изображение GIF кодируется в base64 как кусок электронного письма. Почтовый клиент читает заголовки и декодирует их. Из-за кодировки мы можем быть уверены, что GIF не содержит ничего, что может быть интерпретировано как протокол, и мы избегаем вставки данных, которые SMTP или POP могут найти значимыми.

Эйден Белл
источник
1
Это потрясающе - это объяснение заставило его щелкнуть. Это не запутывание или сжатие данных, а просто использование специальных последовательностей, которые можно интерпретировать как протокол.
Патрик
13

Base64 вместо экранирования специальных символов

Я приведу совсем другой, но реальный пример: я пишу код JavaScript для запуска в браузере. HTML-теги имеют значения идентификатора, но существуют ограничения на то, какие символы допустимы в идентификаторе.

Но я хочу, чтобы мой идентификатор без потерь ссылался на файлы в моей файловой системе. На самом деле файлы могут содержать самые разные странные и замечательные символы: восклицательные знаки, акцентированные символы, тильду и даже смайлики! Я не могу сделать это:

<div id="/path/to/my_strangely_named_file!@().jpg">
    <img src="http://myserver.com/path/to/my_strangely_named_file!@().jpg">
    Here's a pic I took in Moscow.
</div>

Предположим, я хочу запустить такой код:

# ERROR
document.getElementById("/path/to/my_strangely_named_file!@().jpg");

Я думаю, что этот код потерпит неудачу при выполнении.

С Base64 я могу сослаться на что-то сложное, не беспокоясь о том, какой язык допускает какие специальные символы и какие нужно экранировать:

document.getElementById("18GerPD8fY4iTbNpC9hHNXNHyrDMampPLA");

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

Хотел бы я знать о Base64 лет назад. Я бы не рвал на себе волосы с помощью ' encodeURIComponent' иstr.replace(‘\n’,’\\n’)

SSH передача текста:

Если вы пытаетесь передать сложные данные через ssh (например, файл точек, чтобы вы могли получить персонализацию своей оболочки), удачи вам в этом без Base 64. Вот как вы это сделали бы с Base 64 (я знаю, вы можете использовать SCP, но для этого потребовалось бы несколько команд - что усложняет привязку клавиш для sshing на сервер):

Шридхар Сарнобат
источник
12

Один из примеров того, когда я нашел это удобным, был при попытке встроить двоичные данные в XML . Синтаксический анализатор SAX неверно истолковал некоторые двоичные данные, поскольку эти данные могли быть буквально чем угодно, включая специальные символы XML. Base64, кодирующее данные на передающей стороне и декодирующее их на принимающей стороне, решило эту проблему.

Билл Ящерица
источник
1
+1 - но это ни в коем случае не специфично для SAX. Это может произойти с любым XML-парсером, например DOM или XLINQ.
Билли ONEAL
1
@ Билли: Да, абсолютно. Мне просто довелось использовать SAX-парсер для этого приложения.
Билл Ящерица
Различные механизмы, например, синтаксический анализатор SAX, могут интерпретировать некоторые значения ASCII по-разному (разные управляющие символы). Итак, идея здесь в том, чтобы использовать подмножество ASCII, которое имеет общее значение универсально. Правильно?
Лазер
1
@Lazer: Верно. Некодированные двоичные данные будут содержать управляющие символы случайно, когда вы попытаетесь интерпретировать их как ASCII (чего в данном случае не было).
Билл Ящерица
10

Большинство компьютеров хранят данные в 8-битном двоичном формате, но это не является обязательным требованием. Некоторые машины и средства передачи могут обрабатывать только 7 бит (или, возможно, даже меньше) одновременно. Такой носитель будет интерпретировать поток кратными 7 битам, поэтому, если вы отправите 8-битные данные, вы не получите то, что ожидаете с другой стороны. Base-64 - это всего лишь один из способов решения этой проблемы: вы кодируете входные данные в 6-битный формат, отправляете их по своему носителю и декодируете обратно в 8-битный формат на принимающей стороне.

Касабланка
источник
3
Почему это проблема, если поток прерывается после 7 бит? В конце концов, другая машина будет иметь все данные, полученные через поток, она может затем выбрать 8-битный формат для отображения? Что не так с моим разумом!
Маллаудин
6

В дополнение к другим (несколько длинным) ответам: даже игнорируя старые системы, которые поддерживают только 7-битный ASCII, основными проблемами с предоставлением двоичных данных в текстовом режиме являются:

  • Новые строки обычно преобразуются в текстовом режиме.
  • Нужно быть осторожным, чтобы не рассматривать NUL-байт как конец текстовой строки, что слишком легко сделать в любой программе с линией Си.
jamesdlin
источник
Есть также управляющие символы, такие как ^ C, ^ D и ^ Z, которые интерпретируются как конец файла на некоторых платформах.
Ден04
5

Что означает «средства массовой информации, предназначенные для работы с текстовыми данными»?

Эти протоколы были разработаны для обработки текста (часто только на английском языке) вместо двоичных данных (например, изображений .png и .jpg).

Они могут иметь дело с бинарным => они могут иметь дело с чем угодно.

Но обратное неверно. Протокол, разработанный для представления текста, может неправильно обрабатывать двоичные данные, которые содержат:

  • Байты 0x0A и 0x0D, используемые для окончаний строк, различаются в зависимости от платформы.
  • Другие управляющие символы, такие как 0x00 (NULL = терминатор строки C), 0x03 (END OF TEXT), 0x04 (END OF TRANSMISSION) или 0x1A (конец файла DOS), которые могут преждевременно сигнализировать об окончании данных.
  • Байты выше 0x7F (если протокол, который был разработан для ASCII).
  • Последовательности байтов, которые являются недействительными UTF-8.

Таким образом, вы не можете просто отправлять двоичные данные по текстовому протоколу. Вы ограничены байтами, которые представляют неконтролируемые неконтролирующие символы ASCII, которых насчитывается 94. Причина, по которой Base 64 была выбрана, заключалась в том, что она быстрее работает со степенями двойки, а 64 - самая большая, которая работает ,

Хотя один вопрос. Как эти системы все еще не согласны с общей техникой кодирования, такой как UTF-8?

В Интернете, по крайней мере, они в основном есть. Большинство сайтов используют UTF-8 .

Проблема на Западе состоит в том, что существует много старых программ, которые предполагают, что 1 байт = 1 символ и не могут работать с UTF-8.

Проблема на Востоке заключается в их привязанности к кодировкам, таким как GB2312 и Shift_JIS.

И тот факт, что Microsoft, похоже, все еще не справился, выбрав неправильную кодировку UTF. Если вы хотите использовать Windows API или библиотеку времени выполнения Microsoft C, вы ограничены кодировкой UTF-16 или кодировкой ANSI для локали. Это делает болезненным использование UTF-8, потому что вам приходится все время конвертировать.

dan04
источник
5

Почему / Как мы используем кодировку Base64?

Base64 - одна из схем кодирования двоичного текста с эффективностью 75%. Он используется для того, чтобы типичные двоичные данные (такие как изображения) могли безопасно отправляться по устаревшим «не 8-битным чистым» каналам. В более ранних сетях электронной почты (до начала 1990-х годов) большинство сообщений электронной почты представляли собой простой текст в 7-битном наборе символов US-ASCII. Так много ранних стандартов протокола связи были разработаны для работы по "7-битным" каналам связи "не 8-битным чистым". Эффективность схемы - это соотношение между количеством битов на входе и количеством битов на кодированном выходе. Шестнадцатеричное (Base16) также является одной из схем двоичного кодирования с эффективностью 50%.

Шаги кодирования Base64 (упрощенно):

  1. Двоичные данные расположены в виде непрерывных кусков по 24 бита (3 байта) каждый.
  2. Каждый 24-битный блок сгруппирован в четыре части по 6 бит в каждой.
  3. Каждая 6-битная группа преобразуется в соответствующие им значения символов Base64, то есть кодировка Base64 преобразует три октета в четыре кодированных символа. Отношение выходных байтов к входным байтам составляет 4: 3 (33% накладных расходов).
  4. Интересно, что одни и те же символы будут кодироваться по-разному в зависимости от их положения в трехоктетной группе, которая кодируется для получения четырех символов.
  5. Получатель должен будет отменить этот процесс, чтобы восстановить исходное сообщение.
Муштак Хуссейн
источник
3

Что означает «средства массовой информации, предназначенные для работы с текстовыми данными»?

Назад в тот день, когда ASCII управлял миром, имея дело со значениями не-ASCII, была головная боль. Люди прыгали через всевозможные обручи, чтобы передать их по проводам, не теряя информацию.

dirkgently
источник
3
На самом деле, в те времена ASCII даже не использовался повсеместно. Многие протоколы имели отдельный текстовый режим и двоичный режим для передачи данных, к сожалению, электронной почты тогда не было. Текстовый режим необходим именно потому, что ни одна текстовая кодировка не управляет миром, а не ASCII; каждая компьютерная сеть имеет свою любимую кодировку, поэтому существуют шлюзы, в задачу которых входит преобразование обмениваемого текста в локальную кодировку, чтобы японская компания могла отправлять электронную почту американскому бизнес-консультанту без использования mojibake. Это преобразование, очевидно, нежелательно при отправке двоичных данных.
Ли Райан