Есть ли способ поместить вредоносный код в регулярное выражение?

139

Я хочу добавить на свою общедоступную веб-страницу возможность поиска по регулярным выражениям. Нужно ли мне что-либо делать, кроме HTML- кодирования вывода, для защиты от злонамеренного ввода пользователя?

Поисковые запросы Google заполнены людьми, которые решают обратную задачу - используя регулярные выражения для обнаружения вредоносного ввода, - что меня не интересует. В моем сценарии вводимые пользователем данные являются регулярными выражениями.

Я буду использовать библиотеку Regex в .NET (C #).

Мэтью Мартин
источник
4
Это может зависеть от того, какой язык и / или библиотеку регулярных выражений вы используете.
aschepler 02
Еще несколько материалов для чтения: ReDoS на OWASP , ReDoS на Википедии
joeytwiddle

Ответы:

216

Проблемы, связанные с отказом в обслуживании

Наиболее частая проблема с регулярными выражениями - это атака отказа в обслуживании через патологические шаблоны, которые становятся экспоненциальными - или даже суперэкспоненциальными! - и, похоже, на решение уходит вечность. Они могут отображаться только для определенных входных данных, но обычно можно создать такие, в которых это не имеет значения.

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

Отличная статья Расса Кокса 2007 года о сопоставлении регулярных выражений может быть простым и быстрым (но медленным в Java, Perl, PHP, Python, Ruby и т. Д.) Рассказывает о том, как большинство современных NFA, похоже, происходят из кода Генри Спенсера. , страдают от серьезного снижения производительности, но там, где NFA в стиле Томпсона не имеет таких проблем.

Если вы допускаете только шаблоны, которые могут быть решены с помощью DFA, вы можете скомпилировать их как таковые, и они будут работать быстрее, возможно, намного быстрее. Однако на это нужно время . В статье Кокса упоминается этот подход и связанные с ним проблемы. Все сводится к классическому компромиссу между пространством и временем.

С DFA вы тратите больше времени на его создание (и выделение большего количества состояний), тогда как с NFA вы тратите больше времени на его выполнение, так как это может быть несколько состояний одновременно, а возврат с возвратом может съесть ваш обед - и ваш процессор.

Решения отказа в обслуживании

Вероятно, наиболее разумный способ справиться с этими паттернами, которые находятся на проигрыше в гонке с тепловой смертью вселенной, - это обернуть их таймером, который эффективно устанавливает максимальное количество времени, разрешенное для их выполнения. Обычно это намного меньше тайм-аута по умолчанию, предоставляемого большинством HTTP-серверов.

Существуют различные способы их реализации, начиная от простого alarm(N)на уровне C и заканчивая своего рода try {}блокировкой перехвата исключений тревожного типа, вплоть до порождения нового потока, специально созданного с встроенным в него ограничением времени.

Выноски кода

В языках регулярных выражений, которые допускают обозначения кода, должен быть предусмотрен некоторый механизм для разрешения или запрещения их из строки, которую вы собираетесь скомпилировать . Даже если выноски кода предназначены только для кода на языке, который вы используете, вы должны ограничить их; им не обязательно иметь возможность вызывать внешний код, хотя, если они могут, у вас есть гораздо более серьезные проблемы.

Например, в Perl нельзя иметь обозначения кода в регулярных выражениях, созданных путем интерполяции строк (как это было бы, поскольку они компилируются во время выполнения), если use re "eval";в текущей области не активна специальная прагма с лексической областью видимости.

Таким образом, никто не сможет прокрасться в вызове кода для запуска таких системных программ, как rm -rf *, например. Поскольку выноски кода очень чувствительны к безопасности, Perl по умолчанию отключает их для всех интерполированных строк, и вам придется изо всех сил, чтобы снова включить их.

Пользовательские \ P {roperties}

Там остается еще одна защиты чувствительной проблемы , связанная со свойствами Unicode-стиль - как \pM, \p{Pd}, \p{Pattern_Syntax}или \p{Script=Greek}- что может существовать в некоторых регулярных выражениях компиляторов, поддержка, нотация.

Проблема в том, что в некоторых из них набор возможных свойств может быть расширен пользователем. Это означает, что вы можете иметь настраиваемые свойства, которые представляют собой фактические вызовы кода для именованных функций в некотором конкретном пространстве имен, например \p{GoodChars}или \p{Class::Good_Characters}. Возможно, стоит посмотреть, как ваш язык обрабатывает их.

Песочница

В Perl изолированное отделение через Safeмодуль дает контроль над видимостью пространства имен. Другие языки предлагают аналогичные технологии песочницы. Если такие устройства доступны, вы можете изучить их, потому что они специально разработаны для ограниченного выполнения ненадежного кода.

Христос
источник
4
Преобразование NFA-> DFA может вызвать экспоненциальный взрыв состояний, превратив временную DoS-атаку в космическую DoS, а также затраты времени на генерацию экспоненциального числа состояний.
Барри Келли
но, вероятно, ему не понадобятся все возможности регулярных выражений. Что вы думаете об ограничении возможностей регулярных выражений, как это сделал Google: google.com/intl/en/help/faq_codesearch.html#regexp
systemsfault
1
@Barry Совершенно верно. Я думал о стратегии Расса Кокса, описанной в одной из его статей, по постепенному компилированию частей NFA в эквивалентный DFA, но отбрасыванию его, если он станет слишком большим. Но в DFA нет серебряной пули, даже если Томпсон действительно доказал, что это эквивалентно NFA, потому что в какой-то момент вам придется заплатить огромную сумму. Время, потраченное на то, чтобы выпросить у операционной системы больше места, и сопутствующие затраты на настройку таблицы страниц иногда могут сдвинуть шкалу балансировки в другую сторону и сделать преобразование времени в пространство менее привлекательным, чем могло бы быть.
tchrist
20

В дополнение к превосходному ответу Криста: тот же Расс Кокс, который написал страницу «Регулярное выражение», также выпустил код! re2 - это библиотека C ++, которая гарантирует время выполнения O (length_of_regex) и настраиваемый предел использования памяти. Он используется в Google, чтобы вы могли ввести регулярное выражение в поиск по коду Google - это означает, что он прошел боевые испытания.

Брайан Блониарц
источник
2
Действительно так. Вы можете заменить re2 в движок регулярных выражений Perl с помощью модуля, и он будет использовать re2, если возможно, и Perl, если нет. Работает неплохо.
tchrist
6

Вы захотите прочитать эту статью:

Небезопасное переключение контекста: прививка регулярных выражений для обеспечения живучести В статье больше говорится о том, что может пойти не так с механизмами регулярных выражений (например, PCRE), но он может помочь вам понять, с чем вы сталкиваетесь.

Брюс Эдигер
источник
1
Вот совет по безопасности кода GNU libc regcomp (3): securityreason.com/achievement_securityalert/93 Как своевременно! По крайней мере, в Linux уязвимость легко продемонстрировать: grep -E ". * {10,} {10,} {10,} {10,} {10,}"
Брюс Эдигер,
5

Вы должны беспокоиться не только о самом сопоставлении, но и о том, как вы проводите сопоставление. Например, если ваш ввод проходит через какую-то фазу eval или подстановку команд на пути к обработчику регулярных выражений, может быть код, который выполняется внутри шаблона. Или, если ваш синтаксис регулярного выражения допускает встроенные команды, вам тоже следует опасаться этого. Поскольку вы не указали язык в своем вопросе, трудно сказать наверняка, каковы все последствия для безопасности.

Брайан Окли
источник
1

Хороший способ проверить ваши RegEx на наличие проблем с безопасностью (по крайней мере, для Windows) - это инструмент фаззинга SDL RegEx , недавно выпущенный Microsoft. Это может помочь избежать патологически плохой конструкции RegEx.

RandomNickName42
источник