Как защитить от диакритики, такой как текст Zalgo

huh?

Изображенный выше персонаж был опубликован в Твиттере несколько месяцев назад Микко Хиппоненом, экспертом по компьютерной безопасности, известным своей работой над компьютерными вирусами и беседами TED по компьютерной безопасности. Что касается SO, я буду публиковать только изображения, но вы поняли идею. Это, очевидно, не то, что вы хотели бы распространять по вашему сайту и пугать посетителей.

При дальнейшем осмотре персонаж выглядит как буква тайского алфавита в сочетании с более чем 87 диакритическими знаками (есть ли предел?!). Это заставило меня задуматься о безопасности, локализации и о том, как можно справиться с такого рода вводом. Мои поиски привели меня к этому вопросу о стеке и, в свою очередь, к сообщению в блоге Майкла Каплана о разборке диакритических знаков. В нем он демонстрирует, как можно разложить строку на ее "базовые" символы (здесь для краткости упрощено):

StringBuilder sb = new StringBuilder();
foreach (char c in "façade".Normalize(NormalizationForm.FormD))
{
    if (char.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
        sb.Append(c);
}
Response.Write(sb.ToString()); // facade 

Я могу видеть, как это было бы полезно в некоторых случаях, но с точки зрения пользовательского ввода, это исключило бы ВСЕ диакритические знаки. Как указывает Каплан, удаление диакритических знаков в некоторых языках может полностью изменить значение слова. Возникает вопрос: как разрешить некоторые диакритические знаки в пользовательском вводе/выводе, но исключить другие крайние случаи, такие как характер Mikko Hyppönen über?

Ответ 1

есть ли предел?!

По сути, не в Юникоде. В UAX-15 существует концепция формата Stream-Safe, который устанавливает ограничение в 30 объединителей... Строки Unicode в общем случае не гарантируются как Stream-Safe, но это, безусловно, может восприниматься как признак того, что Unicode не намеревайтесь стандартизировать новые символы, которые требуют кластера графемы дольше.

30 все еще очень много. Самым длинным из известных кластеров графем на естественном языке является тибетский Hakṣhmalawarayaṁ на 1 базе плюс 8 сумматоров, поэтому сейчас было бы разумно нормализовать NFD и запретить любую последовательность из более чем 8 сумматоров подряд.

Если вам небезразличны только общепринятые западноевропейские языки, вы можете снизить их до 2. Таким образом, возможно, что-то компромиссное между ними.

Ответ 2

Кажется, я нашел решение, используя NormalizationForm.FormC вместо NormalizationForm.FormD. Согласно MSDN:

[FormC] Указывает, что строка Юникода нормируется с использованием полного каноническое разложение, за которым следует замена последовательностей их первичные композиты, если это возможно.

Я считаю, что это означает, что он разлагает символы в их базовую форму, а затем перекомпоновывает их на основе набора правил, которые остаются согласованными. Полагаю, это полезно для сравнения, но в моем случае это работает отлично. Символы, такие как ü, é и Ä, разлагаются/рекомпозируются точно, а фиктивные символы не могут быть перекомпонурованы и, таким образом, остаются в их базовой форме:

enter image description here

Ответ 3

Здесь регулярное выражение, которое должно вылавливать все Zalgo, включая тех, которые обойдены в "нормальном" диапазоне.

([\u0300–\u036F\u1AB0–\u1AFF\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F\u0483-\u0486\u05C7\u0610-\u061A\u0656-\u065F\u0670\u06D6-\u06ED\u0711\u0730-\u073F\u0743-\u074A\u0F18-\u0F19\u0F35\u0F37\u0F72-\u0F73\u0F7A-\u0F81\u0F84\u0e00-\u0eff\uFC5E-\uFC62]{2,})

Самое сложное - это определить их, как только вы это сделаете, - множество решений.

Надеюсь, это сэкономит вам время.