Юникод исключительно сложен. Мало кто знает все хитрости: от невидимых символов и контрольных знаков до суррогатных пар и комбинированных эмодзи (когда при сложении двух знаков получается третий). Стандарт включает 216 кодовых позиций в 17-ти плоскостях. По сути, изучение Юникода можно сравнить с изучением отдельного языка программирования.
Неудивительно, что веб-разработчики упускают из вида некоторые нюансы. С другой стороны, злоумышленники могут использовать особенности Юникода в своих целях, что и делают.
Специалист по безопасности Джон Грейси продемонстрировал на примере GitHub баг проверки адреса электронной почты для восстановления забытого пароля. Подобные баги можно встретить и на других сайтах.
Джон Грейси объясняет, что такое «коллизия трансляции знаков», когда два разных знака после конвертации транслируются в один и тот же знак.
В данном случае он использовал турецкий символ 'ı' ('i' без точки), который транслируется в латинскую 'i', так что почтовый адрес John@Gıthub.com
после обработки превращается в John@Github.com
:
'ß'.toLowerCase() // 'ss'
'ß'.toLowerCase() === 'SS'.toLowerCase() // true
// Note the Turkish dotless i
'John@Gıthub.com'.toUpperCase() === 'John@Github.com'.toUpperCase()
Такие коллизии можно найти по всем плоскостям Юникода: вот полный список.
Нас интересуют в первую очередь те знаки, которые транслируются в латинские символы. Таких всего одиннадцать вариантов. На третьем месте в таблице как раз турецкий знак 'i' без точки.
Знак | Кодовая точка | Результат |
---|---|---|
ß | 0x00DF | SS |
ı | 0x0131 | I |
ſ | 0x017F | S |
ff | 0xFB00 | FF |
fi | 0xFB01 | FI |
fl | 0xFB02 | FL |
ffi | 0xFB03 | FFI |
ffl | 0xFB04 | FFL |
ſt | 0xFB05 | ST |
st | 0xFB06 | ST |
K | 0x212A | k |
GitHub позволял злоумышленнику получить пароль от чужого аккаунта, потому что процедура восстановления забытого пароля работала некорректно.
В рамках этой процедуры выполнялось сравнение введённого адреса электронной почты с адресом, который хранится в базе. Алгоритм проверки:
- Введённый адрес переводится в нижний регистр с помощью метода toLowerCase.
- Введённый адрес сравнивается с адресом в базе зарегистрированных пользователей.
- Если найдено совпадение, пароль из базы данных высылается на введённый адрес.
Очевидно, разработчики не знали о коллизии трансляции адресов при использовании метода toLowerCase
.
В данном случае исправить ошибку просто. Достаточно высылать пароль не на введённый адрес, а на адрес из базы данных.
Конечно, это не полное исправление ошибки, а только быстрый патч. Более полным решением будет трансляция в Punycode для проверки: John@Gıthub.com
→ xn—john@gthub-2ub.com
. Punycode был разработан для однозначного преобразования доменных имен в последовательность ASCII-символов. Адрес электронной почты можно проверять таким же способом, но большинство веб-приложений этого не делает.
За найденную уязвимость Джон Грейси получил денежное вознаграждение и 2500 очков в рейтинг, хотя ему ещё далеко до главного гитхабовского хакера Александра Добкина <img src=404 onerror=alert(document.domain)>: пользователь с таким необычным именем заработал уже 30 750 очков, в том числе за выполнение произвольного кода на серверах GitHub, на которых генерируются страницы GitHub Pages.
Сбой в мессенджере при получения эмодзи с чёрной точкой (Messenger в iOS, WhatsApp под Android)
Связанные с Юникодом баги имеют такое свойство, что их можно встретить в любом приложении, которое обрабатывает текст, введённый пользователем. Уязвимости есть и в веб-приложениях, и в нативных программах под Android и iOS. Одним из самых известных стал баг iOS от 2015 года, когда несколько знаков Юникода в текстовом сообщении вызывали сбой операционной системы. В прошлом году похожий юникодовский баг обнаружили в iOS 11.3, он известен как «чёрная точка». Похожий сбой происходил в приложении WhatsApp под Android, если прикоснуться к эмодзи.
Автор: GlobalSign_admin