Конвертер кодировок текста

UTF-7: Юникод, переодетый в латиницу

Перекодировать текст из UTF-7 ↓

Середина 1990-х годов. Электронная почта уже связывает университеты и компании по всему миру, но устроена она по правилам двадцатилетней давности. Протокол передачи писем гарантирует сохранность только семи бит из каждого байта, а значит, без потерь по цепочке серверов проходят лишь символы ASCII. Всё, что выходит за эти рамки, почтовый узел где-нибудь на полпути вправе обрезать или исказить. В то же самое время набирает силу Юникод, обещающий единую таблицу для всех письменностей. Возникает закономерный вопрос: как отправить письмо с иероглифами или кириллицей по каналу, который понимает только английский алфавит?

Ответ предложили в 1994 году Дэвид Голдсмит и Марк Дэвис, инженеры, стоявшие у истоков самого Юникода. Их разработка получила название UTF-7, а в подзаголовке описывающего её документа стояли слова "безопасный для почты формат". Цифра семь в названии означает не количество бит на символ, как можно подумать, а то, что результат состоит исключительно из семибитных знаков. Любой текст на любом языке после такого преобразования выглядит как строчка из латинских букв, цифр и пары значков, и ни один почтовый сервер не найдёт в ней ничего подозрительного.

Работает это следующим образом. Латинские буквы, цифры и основные знаки препинания записываются как есть. Когда же в тексте встречается что-то иное, кодировка ставит знак плюс и переходит в особый режим: символы переводятся в UTF-16, полученные байты перекладываются в Base64, то есть в набор из шестидесяти четырёх безобидных знаков, а в конце ставится дефис, возвращающий обычный режим. Русское слово "Привет" в UTF-7 принимает вид "+BB8EQAQ4BDIENQRC-". Сам плюс, раз уж он стал служебным, приходится записывать сочетанием из плюса и дефиса.

Задумка выглядела изящно: английские фрагменты остаются читаемыми, а всё прочее аккуратно упаковывается. Но изящество обернулось сложностью. Чтобы прочитать текст, программе нужно отслеживать, в каком режиме она находится, разбирать шестибитные порции, собирать из них шестнадцатибитные единицы, а при необходимости ещё и склеивать суррогатные пары. Одну и ту же фразу можно закодировать несколькими правильными способами, поскольку часть знаков разрешено и оставлять как есть, и прятать в Base64. Сравнивать и искать такие строки без предварительной расшифровки нельзя. По расходу места UTF-7 тоже не блистала: на русскую букву уходит около двух и двух третей байта против двух в UTF-8 и одного в KOI8-R.

Пока UTF-7 пыталась закрепиться, почтовый мир решил проблему иначе. Стандарт MIME позволил указывать в заголовке письма любую кодировку и заворачивать содержимое целиком в Base64 или в формат quoted-printable. Затем серверы научились пропускать восьмибитные данные без искажений. Русскоязычная почта благополучно ходила в KOI8-R и Windows-1251, а позднее перешла на UTF-8. Отдельная хитроумная кодировка для писем оказалась попросту не нужна, и в Юникод как официальный формат она так и не вошла, оставшись описанной лишь в документах серии RFC.

В одном месте UTF-7 всё-таки прижилась, причём надолго. Протокол IMAP, по которому почтовые программы получают доступ к ящику на сервере, использует её слегка изменённый вариант для названий папок. Плюс там заменён на амперсанд, а косая черта на запятую, чтобы не путать служебные знаки с разделителями в путях. Поэтому папки с русскими именами, такие как "Входящие" или "Отправленные", хранятся на сервере в виде длинных строк, начинающихся с амперсанда. Пользователь этого не видит, но администраторы и авторы почтовых программ сталкиваются с такими названиями постоянно.

Широкую известность UTF-7 принесла вовсе не почта, а история с безопасностью сайтов. Защита от внедрения вредоносного кода обычно строится на том, что из пользовательского ввода удаляются или обезвреживаются угловые скобки, без которых не написать тег. Но в UTF-7 открывающая скобка записывается как "+ADw-", а закрывающая как "+AD4-", и для фильтра это ничем не примечательные буквы. Если страница не объявляла свою кодировку, некоторые браузеры пытались угадать её сами. Злоумышленнику достаточно было оставить в комментарии строку в UTF-7 и подтолкнуть браузер к нужной догадке, после чего безобидный на вид текст превращался в работающий сценарий.

Атака оказалась настолько удобной, что на протяжении 2000-х годов её обнаруживали на крупнейших сайтах и в популярных веб-приложениях. Итог был предсказуем: разработчики браузеров один за другим отключили поддержку UTF-7, а действующие веб-стандарты прямо запрещают её использовать. Вслед за ними от кодировки стали отказываться и программные платформы, пометив соответствующие функции как устаревшие и небезопасные. Заодно эта история научила веб-мастеров простому правилу: кодировку страницы нужно объявлять всегда и явно, не оставляя браузеру простора для фантазии.

Сегодня UTF-7 интересна главным образом как урок. Она показывает, что остроумное решение сиюминутной проблемы может устареть раньше, чем успеет распространиться, а лишняя гибкость формата легко превращается в уязвимость. UTF-8, появившаяся примерно в те же годы, пошла другим путём: не подстраивалась под ограничения старых каналов, а дождалась, пока каналы подстроятся под неё. Если же вам попалась строка, в которой между плюсом и дефисом зажата бессмысленная на вид последовательность латинских букв и цифр, знайте, что перед вами редкий гость из прошлого, и любой конвертер кодировок без труда вернёт ему исходный облик.