Нормализация Unicode

    Однажды мне пришлось наблюдать, как спамеры очень интересным образом обходят спам-фильтр. Вместо традиционного URL типа «example.com», ссылка выглядела так:
    http://example.com
    Ссылка с подобной изощрённой точкой работает в IE7, FF3, Opera 9.5, Safari 3, Google Chrome и не работает в IE6.

    UAX #15: Unicode Normalization Forms


    Немного подумав, я стал искать решение проблемы. Поскольку точка явно относилась к классу эзотерических Unicode-символов (как я узнал впоследствии, это — японская полноширинная точка), я решил заглянуть в соотвествующий стандарт, и там нашёл ответ на волнующий меня вопрос. Оказывается, существуют процедуры нормализации текста, после которого он пригоден для сравнения.

    Композиция, декомпозиция, и преобразование экзотических символов


    В Unicode есть 4 вида нормализации. Первые два из них — композиция и декомпозиция — позволяют справиться со следующими проблемами:
    • В Unicode одна и таже сложная буква типа «Ç» может быть представлена в двух формах: в виде единой буквы и в виде базовой буквы («C») и модификаторов. Процесс, при котором все буквы по возможности объединяются в одну, называется композицией (Normalization Form C, далее — NFC), а процесс, при котором все буквы по возможности разбиваются на модификаторы — декомпозицией (Normalization Form D, далее — NFD).
    • Если модификаторов несколько, то они могут быть разрбросаны в разном порядке.
    • Одна и та же буква может иметь несколько вариантов (например, «Ω» и «Ω»)

    Чтобы пояснить всё вышесказанное, приведу несколько иллюстраций из стандарта:
    NFC и NFD
    NFC и NFD
    Далее. Существует множество символов, типа вышепреведенной точки «.», которые выглядят очень похожими на другие и могут быть подло использованы спамерами. Специально для таких случаев существует Normalization Form KC (NFKC) и Normalization Form KD (NFKD), которые, помимо (де)композиции, нормализуют следующие символы:
    • Изощрённые шрифты (ℍ и ℌ)
    • Кружки (①)
    • Изменённый размер и угол поворота (カ и カ, ︷ и {)
    • Степени (⁹ и ₉)
    • Дроби (¼)
    • Другие (™)

    Посмотрим в действии:
    NFKC и NFKD
    Таким образом, NFKC/NFKD — это именно то, что нам надо для защиты от спамеров и прочей нечисти. Осталось только прикрутить это к программе.

    Реализация

    1. Для C/C++ есть библиотека ICU — я думаю, что большинство, кому приходилось работать с Unicode под C/C++, знают о ней. Для тех, кто не знает: вот официальный сайт. В ICU вся нормализация выполняется через класс Normalizer.
    2. Для Java есть та же ICU и тот же класс Normalizer
    3. Для PHP всё сложнее. Я знаю как минимум два способа:
      • Использовать класс Normalizer из библиотеки intl.
      • Если по каким-то причинам невозможно использовать библиотеку intl, можно взять готовую реализацию из MediaWiki (через SVN), которая реализована там в виде независимой подсистемы.

    Приведу банальный пример (в связи с основным языком и основным проектом буду использовать последнюю указанную мной библиотеку):
    <?php
    require_once( 'normal/UtfNormal.php' );
    $input "http://example.com";
    echo 
    "{$input}\n";
    echo 
    UtfNormal::toNFKC$input ) . "\n";


    Выводит эта программа следующее:
    http://example.com
    http://example.com
    

    Итог


    Как мы видим, NFKC/NFKD позволяет нам урезать возможности для «игры с буквами», и незаменим в спам-фильтрах и матоблокировщиках. NFC вдобавок позволяет нам сжать текст.
    Поделиться публикацией
    Комментарии 27
      +7
      Очень интересно, спасибо :)
        +5
        Не знал. Спасибо за статью.
          0
          почитал, почерпнул много нового, говорю спасибо ^_^
            0
            Спасибо за статью, интерсно. =)
              +2
              Одно но: NFKx могут вам текст изуродовать до полной неузнаваемости, так как прератят X² в X2. Но для спама это, конечно, вариант.
                0
                Но ведь можно текст «изуродовать», проверить на спам и, если всё ок, то записать его в базе/файл в изначальном виде…
                  +4
                  Я собственно о том, что именно так и надо делать. Записывать в базу NFC можно, NFKC — ни в коем случае.
                0
                Насамом деле очень и очень познавательно!
                  0
                  В дополнение статья на вики ru.wikipedia.org/wiki/Unicode
                    +1
                    Ага. И ещё туточки много чего написано. Гулять, так гулять, да?
                      0
                      Погуглить никогда не вредно)
                    0
                    Спасибо, очень познавательно и интересно
                      +3
                      Спасибо за статью. Примечательная особенность Хабра в отличие от тех же обучающих и советующих сайтов на английском — люди в ASCII-мире не задумываются о проблемах национальных кодировок и толковой помощи от них не жди.
                        +4
                        И именно поэтому весь мир переходит на УТФ8 в основном стараниями тех самых ASCII-жителей :)
                        +4
                        Novikov знает? :-)
                          +2
                          А еще decomposed unicode используется по-умолчанию в MacOSX.
                          Я написал модуль для прозрачного перекодирования NFD -> NFC для macosx, если кому будет полезно.

                          code.google.com/p/nfd2nfc/
                            0
                            Вы могли бы поподробней рассказать как пользоваться вашим модулем?
                            Очень страдаю от decomposed unicode на Маке, в частности не могу нормально обмениваться файлами через NAS с коллегами из-за того, что Мак не понимает precomposed букву «й» в названиях файлов и наоборот.
                            +7
                            Однажды я прогуливался по мосту и увидел человека, стоящего на краю, который собирался прыгнуть. Я быстро подбежал к нему и сказал «Стой! Не делай этого!»
                            «Я ничего не могу поделать» — плакал он, «я потерял интерес к жизни».
                            «Чем ты зарабатываешь на жизнь?» — спросил я.
                            Он сказал: «Я разрабатываю технические требования для веб-сервисов»
                            «Я тоже!» — сказал я. «Ты используешь REST-сервисы или SOAP-сервисы?»
                            Он сказал: «REST-сервисы»
                            «Я тоже!» — сказал я. «Ты используешь текстовой формат XML или бинарный формат XML?»
                            Он сказал: «текстовой формат XML»
                            «Я тоже!» — сказал я. «Ты используешь XML 1.0 или XML 1.1?»
                            Он сказал: «XML 1.0»
                            «Я тоже!» — сказал я. «Ты используешь UTF-8 или UTF-16?»
                            Он сказал: «UTF-8»
                            «Я тоже!» — сказал я. «Ты используешь нормализацию юникода формы C или нормализацию юникода формы KC?»
                            Он сказал: «нормализацию юникода формы KC»
                            «Умри, иноверная мразь!» — прокричал я, и столкнул его с моста.

                            tty01.blogspot.com/2004/07/unicode-normalization-form-c.html
                              0
                              У меня вот так вот выглядит:



                              Не подскажете, что нужно сделать, чтобы в фф были нормальные буквы вместо квадратиков? А то надоело уже :( Кстати, это не только в браузере, не нашёл пока ни одного приложения у себя которое бы нормально отображало этот символ, в винде (XP SP2) что-то не так настроено?
                                –1
                                Точка японская
                                  0
                                  У вас поддержка азиатских языков установлена? MS IE до сих пор с Unicode не дружит (интерсно, кстати, как с этим в 8й версии), но в Firefox всё должно быть нормально…
                                    +1
                                    Control Panel->Regional And Language Settings->Languages->Install files for complex scripts and right-to-left languages
                                    Control Panel->Regional And Language Settings->Languages->Install fonts for East Asian languages

                                    P.S. Я исхожу из того что у вас нормальная лицензионная винда: эти самые шрифты много весят, по умолчанию не ставятся и потому являются первым что пираты выбрасывают с CD чтобы сделать сборники типа «8 версий Windows в одном!»…
                                      0
                                      Действительно, что-то я не подумал про поддержку азиатских языков. Может, из-за этого у меня периодически квадратики и возникают. CD нету… эхх, остаюсь я, похоже, без японских точек :)
                                      0
                                      Было такое. Искал долго, в чем же дело. Оказалось — в файрволле. В Аутпосте. После его обновления больше не проявлялось.
                                      0
                                      Вроде, NFK* не приведут национальные символы, к латинице. Т.е. NFKD для русского «ё» = русское «е» + комбинирующий ":", а не латинское «e». Да и для того, что бы канонически разложить Ж, скажем, на >I< надо свою либу писать…
                                        0
                                        А я говорил, что NFKC приводит русскую «e» в английскую? Для полного визуального соответствия можно ещё добавить это, но вот еxample.com вам к example.com браузер не приравняет.
                                      • НЛО прилетело и опубликовало эту надпись здесь

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое