Однажды мне пришлось наблюдать, как спамеры очень интересным образом обходят спам-фильтр. Вместо традиционного URL типа «example.com», ссылка выглядела так:
http://example.com
Ссылка с подобной изощрённой точкой работает в IE7, FF3, Opera 9.5, Safari 3, Google Chrome и не работает в IE6.
Немного подумав, я стал искать решение проблемы. Поскольку точка явно относилась к классу эзотерических Unicode-символов (как я узнал впоследствии, это — японская полноширинная точка), я решил заглянуть в соотвествующий стандарт, и там нашёл ответ на волнующий меня вопрос. Оказывается, существуют процедуры нормализации текста, после которого он пригоден для сравнения.
В Unicode есть 4 вида нормализации. Первые два из них — композиция и декомпозиция — позволяют справиться со следующими проблемами:
Чтобы пояснить всё вышесказанное, приведу несколько иллюстраций из стандарта:
Далее. Существует множество символов, типа вышепреведенной точки «.», которые выглядят очень похожими на другие и могут быть подло использованы спамерами. Специально для таких случаев существует Normalization Form KC (NFKC) и Normalization Form KD (NFKD), которые, помимо (де)композиции, нормализуют следующие символы:
Посмотрим в действии:
Таким образом, NFKC/NFKD — это именно то, что нам надо для защиты от спамеров и прочей нечисти. Осталось только прикрутить это к программе.
Приведу банальный пример (в связи с основным языком и основным проектом буду использовать последнюю указанную мной библиотеку):
Выводит эта программа следующее:
Как мы видим, NFKC/NFKD позволяет нам урезать возможности для «игры с буквами», и незаменим в спам-фильтрах и матоблокировщиках. NFC вдобавок позволяет нам сжать текст.
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).
- Если модификаторов несколько, то они могут быть разрбросаны в разном порядке.
- Одна и та же буква может иметь несколько вариантов (например, «Ω» и «Ω»)
Чтобы пояснить всё вышесказанное, приведу несколько иллюстраций из стандарта:
Далее. Существует множество символов, типа вышепреведенной точки «.», которые выглядят очень похожими на другие и могут быть подло использованы спамерами. Специально для таких случаев существует Normalization Form KC (NFKC) и Normalization Form KD (NFKD), которые, помимо (де)композиции, нормализуют следующие символы:
- Изощрённые шрифты (ℍ и ℌ)
- Кружки (①)
- Изменённый размер и угол поворота (カ и カ, ︷ и {)
- Степени (⁹ и ₉)
- Дроби (¼)
- Другие (™)
Посмотрим в действии:
Таким образом, NFKC/NFKD — это именно то, что нам надо для защиты от спамеров и прочей нечисти. Осталось только прикрутить это к программе.
Реализация
- Для C/C++ есть библиотека ICU — я думаю, что большинство, кому приходилось работать с Unicode под C/C++, знают о ней. Для тех, кто не знает: вот официальный сайт. В ICU вся нормализация выполняется через класс Normalizer.
- Для Java есть та же ICU и тот же класс Normalizer
- Для 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 вдобавок позволяет нам сжать текст.