Взлом аккаунта и юникодные символы

    В техническом блоге «Спотифая» было опубликовано интересное исследование на тему взлома аккаунтов сервиса путём использования особенностей канонизации вводимых пользователем данных. Это стало возможным благодаря тому, чем спотифаевцы гордятся, — полностью юникодному логину. К примеру, пользователь легко может иметь снеговика в качестве имени аккаунта, если он того пожелает. Реализация подобного, впрочем, с самого начала доставляла некоторые неудобства.

    Несколько лет назад в Великую пятницу на форуме техподдержки было опубликовано сообщение о возможности взлома любого аккаунта на сервисе. Представитель компании попросил продемонстрировать это на примере его собственного аккаунта, и через несколько минут ему был задан новый пароль и создан новый плейлист. Это немедленно привлекло внимание нескольких сотрудников, вынужденных провести Пасху в попытках закрыть «дыру». В связи с особенностями уязвимости регистрация новых аккаунтов была временно закрыта.

    Хакер действовал следующим образом: желая взломать аккаунт с именем, скажем, bigbird, он регистрировал аккаунт с именем ᴮᴵᴳᴮᴵᴿᴰ (в Пайтоне эта строчка выглядит как u’\u1d2e\u1d35\u1d33\u1d2e\u1d35\u1d3f\u1d30′). После запроса ссылки на сброс пароля задавался новый пароль, который подходил к аккаунту bigbird.

    Проблема состояла в канонизации имени пользователя, некорректно обрабатывающей запрещенные и эквивалентные символы. Очевидно, что в имени пользователя недопустимы пробелы, а BigBird и bigbird — это один и тот же логин. Первый случай — это обработка запрещенных символов, второй — обработка некоторых символов как эквивалентных друг другу. Подобное достигается канонизацией имени пользователя.

    Если допускаются только символы латинского алфавита (a—z, A—Z), то будет достаточно

    canonical_username = username.lower()


    Таким образом BigBird, Bigbird, bigbird и любые производные будут одним и тем же логином. BigBird в таком случае называют точным именем пользователя, а bigbird — каноническим. При создании аккаунта необходимо, чтобы канонический логин был свободен в системе.

    Обращение в нижний регистр обладает свойством идемпотентности, то есть приложение его к одной и той же строке один и более раз даёт один и тот же результат:

    x.lower() == x.lower().lower()


    С разрешением юникодных символов начинаются разнообразные проблемы. К примеру, внешне трудно отличить Ω от Ω, хотя первое — буква омега, а второе — символ единицы измерения, и в Юникоде это — различные символы. Очевидно, что просто обращения в нижний регистр было бы недостаточно. К счастью для разработчиков, собственной системы канонизации разрабатывать не пришлось, в фреймворке Twisted были необходимые методы, разработанные ещё для XMPP.

    from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep
    def canonical_username(name):
        return nodeprep.prepare(name)


    Идемпотентность обещается в спецификациях. Так в чём же дело? Посмотрим, что случается при вводе ᴮᴵᴳᴮᴵᴿᴰ.

    >>> canonical_username(u'\u1d2e\u1d35\u1d33\u1d2e\u1d35\u1d3f\u1d30')
    u'BIGBIRD'
    >>> canonical_username(canonical_username(u'\u1d2e\u1d35\u1d33\u1d2e\u1d35\u1d3f\u1d30'))
    u'bigbird'


    Как видно, свойство идемпотентности не выполняется для этих символов. Дело в том, что согласно официальной документации учитывались символы Юникода 3.2, в который не входит ни один из символов ᴮᴵᴳᴮᴵᴿᴰ. При регистрации аккаунта после однократного применения функции канонизации создавался логин BIGBIRD, что было допустимо, поскольку каноническое имя не пересекалось с существующим аккаунтом bigbird. При отсылке сообщения на электронную почту с ссылкой для сброса пароля ᴮᴵᴳᴮᴵᴿᴰ канонизировался единожды, поэтому письмо получал пользователь BIGBIRD. Но при использовании ссылки функция канонизации использовалась повторно, что приводило к сбросу пароля для аккаунта bigbird, а не BIGBIRD.

    Уязвимость сначала была исправлена посредством обязательности выполнения условия X==canonical_username(X). Позже была добавлена функция, которая, по сути, лишь выполняла функцию канонизации и отказывала в регистрации, если old_prepare(old_prepare(name)) != old_prepare(name). Проблема же в Twisted была исправлена в версии 11.0.0, и, как оказалось, баг проявлялся лишь при обновлении версии Пайтона с 2.4 к 2.5, что было вызвано изменениями в стандартной библиотеке.

    Подобные случаи в лишний раз подчёркивают необходимость проверки вводимых пользователем данных и избегания негатива при общении между пользователями и сотрудниками. Нашедшие уязвимость хакеры были награждены несколькими месяцами бесплатной премиум-подписки, и это был не первый и не последний случай проблем с особенностями обработки символов. Также не стоит забывать о том, что обновления не всегда сулят избавление от багов и иногда порождают новые.
    Share post

    Similar posts

    Comments 31

      +2
      А почему бы просто не использовать для всех случаев канонизации, в том числе регистрации и сброса пароля, один и тот же способ канонизации — единственный вызов toLower на стороне сервера?
        +4
        Наверное дело в плохой обработки им юникодных символов
          +1
          Сначала канонизировать в обычные символы, а потом уже toLower. И как ни крути, toLower уже самые обычные буквы обрабатывает.
            +2
            Но ведь результат всегда будет одинаковым. Поэтому и невозможно будет зарегистрировать аккаунт ᴮᴵᴳᴮᴵᴿᴰ, если уже есть bigbird, BigBird или BIGᴮᴵᴿᴰ.
          +6
          А почему не хранить эти юникодные логины в базе как есть?
            0
            В статье есть пример с внешне одинаковыми, но разными символами Ω и Ω. Вероятно, таких совпадений слишком много.
              +1
              Ну так в БД и будут лежать разные байтики.
              Проблему я вижу только в том месте, где надо эти логины показывать (в поиске, например).
                +2
                Ну вот отображение — это и есть проблема. Получатся два внешне одинаковых пользователя, что считается недопустимым.
                  +2
                  Тогда ники K12th, k12Th, k12tH и т.п. будут разными. Впрочем, это не проблема — подумаешь, регистрозависимые логины.

                  Проблемы могут возникнуть потом, когда на разных устройствах или операционных системах один и тот же юникодный текст будет кодироваться разными байтами. К примеру букву Й можно закодировать одним символом как \u0419, а можно двумя как \u0415\u0308, т.е. И + ̆. И оба этих способа кодируют одну и ту же букву, просто разными способами.

                  С русским алфавитом такое вряд ли случится, но вот с какими-нибудь иероглифами, которые набираются не одной кнопкой с клавиатуры, а собираются из составляющих, вполне может быть, и поэтому юникод желательно нормализовывать после пользовательского ввода, но обычно никто этим не заморачивается :)
                    +4
                    Опять не вижу проблему — пользователь-то знает, нажимал он Й или набирал И + ̆:)

                    Вообще изначально проблема в том, что логин и ник — это почему-то одно и то же. Если бы, например, в фейсбуке такое было, то женщинам приходилось бы заново там регаться после свадьбы. Слава богу, такого там нет.
                      +2
                      Опять не вижу проблему — пользователь-то знает, нажимал он Й или набирал И + ̆:)
                      Дык это от софта зависит. Вот у себя дома на русской клавиатуре вы нажимаете одну кнопку Й, а где-нибудь в отпуске на клавиатуре в другой ОС вы будете выбирать букву И и модификатор к ней.
                      У меня на клавиатуре для набора символов ô, ò, ó нужно нажимать по две-три клавиши в определённом порядке. Предположу, что в странах, где эти символы есть в алфавите, они набираются одной клавишей.

                      На счёт проблемы с логином согласен — сделали бы логином адрес электронной почты, и проблем бы не было… До тех пор, пока не начнут использовать национальные домены для почты :)
                        +1
                        ô, ò, ó
                        я вижу, что у вас это по одному символу, а не маленькая латинская o + комбинирующий циркумфлекс/гравис/акут. Насчет, допустим, французского языка не знаю, может они и вводят комбинирующие модификаторы, но, например, в польском я ввожу всякие ó, ą и ł одним символом (хоть и двумя клавишами).
                          +2
                          Я думаю, суть вы поняли — символы юникода могут кодироваться разными последовательностями байт и в то же время означать одно и то же, а приведённые мною символы — это просто доступный мне пример, так как неевропейских символов у меня под рукой нет.
                          В тексте последнего комментария набирал я их, кстати, не за компом, а на экранной клавиатуре, долгим тапом и свайпом :)
                            +1
                            Ну, надеюсь, вы меня тоже поняли:)
                            +1
                            Во французской раскладке для частых символов, таких как é, ç, à, è, ù, предусмотрен ввод нажатием одной клавишы. А для ô, î, â, ö ï нужно нажимать последовательность клавиш (просто может кому любопытно).
                              +1
                              Спасибо, мне интересно.
                        +1
                        Вы описываете проблемы нормализации юникодных символов. Алгоритмы нормализации известны и нормализацию могут проводить даже, например, на этапе сохранения данных в СУБД.
                          0
                          Спасибо, Кэп.
                      +1
                      Это одинаковые как внешне, так и по кодированию символы. Я думаю автор ошибся при копировании.
                    –7
                    Мде, раздули проблему на пустом месте!
                    Те, кому не хочется проблем, не разрешают использовать не-ASCII символы в логинах и паролях. А зачастую так вообще ограничение еще строже делается.
                    Ну, а кому хочется приключений на свою пятую точку, разрешают юникод.
                      +17
                      Простите, вы в сбербанке работаете?
                        +1
                        А много Вы знаете программ, которая обрабатывает юникод по стандарту, полностью и абсолютно без багов?

                        stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/6163129#6163129

                        Хотели бы чтобы Ваш банк полностью поддерживал юникод в логинах и паролях?
                          +1
                          А много Вы знаете программ, которая обрабатывает юникод по стандарту, полностью и абсолютно без багов?
                          Почему-то мне кажется, что это проблема не юникода.
                          +2
                          Вот например Chrome самовольно нормализует юникод stackoverflow.com/questions/11176603/how-to-avoid-browsers-unicode-normalization-when-submitting-a-form-with-unicode

                          Программа, описанная в посте это учитывает?

                          Или пароль в одних браузерах подходит, в других нет?

                          Или Вы знаете что в вашем языке программирования нужно сделать чтобы sha/md5 на стороне БД высчитывалось так же как и в языке программирования
                          +3
                          А если я японец? ッ
                            +2
                            логин это не имя. И вообще, лучше уж номер телефона или емейл просить в качестве логина, а уж имя пусть любое пишет.
                            Короче, как мне кажется, в качестве логина должна выступать такой набор символов по которым можно с человеком связаться и запросить подтверждение регистрации.
                              +1
                              И у вас нет латинницы на клавиатуре? Не смешите мои тапочки!!!

                              vinograd19, не надо меня оскорблять!

                              И не надо выдумывать несуществующих оправданий использованию юникода: он нужен лишь в многоязычных документах, да народам с иероглифическим письмом!
                                +1
                                Привет Эдди,

                                Скажи мне какая у тебя локаль? ;)

                                А вообще у японцев какбы хирагана и катакана (и ещё куча других да) в качестве письменности, они набираются латиницей.

                                Разный ник и логин? Берём ник выглядищий похоже (если вообще одинаковые ники запрещены) и выдаём себя за другого человека. Можете посмотреть какой треш творился на говнокод.ру.

                                P.S. Как будто логин через почту более безопасен.
                                  0
                                  Привет.
                                  Сразу тебе ответить не мог (почему? — взгляни в мой профиль на некий показатель, отторгающий этот сайт от делового общения).
                                  А локаль у меня спрашивать не надо: я с линуксом знаком около 12 лет (точно не помню), так что локаль у меня классическая — КОИ8-Р. Другой и не нужно, я ведь не китаец…

                                  // глянул тебе в профиль. Да уж, ты тоже от «ылиты» пострадал …
                            +1
                            Мне вот интересно, как люди вообще додумываются ко взлому, не сама мотивация, а именно сам процесс мышления и тот объем знаний которым должны они обладать. Я сам по себе ИМХО не самый тупой инженер, но это вне моего понимания (
                            Я восхищяюсь такими людьми.
                              +2
                              Сама мысль «у нас юникод разрешён в логине и пароле» незамедлительно вызывает у меня ассоциации со взломом. Хотя уязвимостями и взломами я интересуюсь гораздо меньше некоторых остальных вещей и никогда не тяготел к поиску уязвимостей.

                            Only users with full accounts can post comments. Log in, please.