Отказ в обслуживании с помощью регулярного выражения ( ReDoS ) [ 1] — это атака на алгоритмическую сложность , которая вызывает отказ в обслуживании , предоставляя регулярное выражение и/или входные данные, оценка которых занимает много времени. Атака использует тот факт, что многие [2] реализации регулярных выражений имеют сверхлинейную сложность в худшем случае ; на определенных парах регулярное выражение-входные данные затраченное время может расти полиномиально или экспоненциально по отношению к размеру входных данных. Таким образом, злоумышленник может заставить программу тратить значительное время, предоставляя специально созданное регулярное выражение и/или входные данные. После этого программа замедлится или перестанет отвечать. [3] [4]
Сопоставление регулярных выражений («regex») может быть выполнено путем построения конечного автомата . Regex можно легко преобразовать в недетерминированные автоматы (NFA), в которых для каждого состояния и входного символа может быть несколько возможных следующих состояний. После построения автомата существует несколько возможностей:
Из вышеперечисленных алгоритмов первые два являются проблематичными. Первый является проблематичным, поскольку детерминированный автомат может иметь до состояний, где — число состояний в недетерминированном автомате; таким образом, преобразование из NFA в DFA может занять экспоненциальное время . Второй является проблематичным, поскольку недетерминированный автомат может иметь экспоненциальное число путей длины , так что прохождение по входу длины также займет экспоненциальное время. [7] Последние два алгоритма, однако, не демонстрируют патологического поведения.
Обратите внимание, что для непатологических регулярных выражений проблемные алгоритмы обычно быстры, и на практике можно ожидать, что они « скомпилируют » регулярное выражение за время O(m) и сопоставят его за время O(n); вместо этого моделирование NFA и ленивое вычисление DFA имеют сложность O (m 2 n) в худшем случае. [a] Отказ в обслуживании регулярных выражений происходит, когда эти ожидания применяются к регулярному выражению, предоставленному пользователем, и вредоносные регулярные выражения, предоставленные пользователем, запускают наихудшую сложность сопоставителя регулярных выражений.
Хотя алгоритмы регулярных выражений могут быть написаны эффективным способом, большинство существующих движков регулярных выражений расширяют языки регулярных выражений дополнительными конструкциями, которые не всегда могут быть решены эффективно. Такие расширенные шаблоны по сути вынуждают реализацию регулярных выражений в большинстве языков программирования использовать возврат.
Наиболее серьезный тип проблемы возникает при обратном поиске соответствий регулярных выражений, где некоторые шаблоны имеют время выполнения, которое экспоненциально зависит от длины входной строки. [8] Для строк символов время выполнения составляет . Это происходит, когда регулярное выражение имеет три свойства:
+
, *
) к подвыражению;Второе условие лучше всего объяснить на двух примерах:
(a|a)+$
повторение применяется к подвыражению a|a
, которое может сопоставляться a
двумя способами с каждой стороны чередования.(a+)*$
повторение применяется к подвыражению a+
, которое может соответствовать a
или aa
и т. д.В обоих этих примерах мы использовали $
для сопоставления конец строки, удовлетворяя третьему условию, но для этого также можно использовать другой символ. Например, (a|aa)*c
имеет ту же проблемную структуру.
Все три из приведенных выше регулярных выражений будут демонстрировать экспоненциальное время выполнения при применении к строкам формы . Например, если вы попытаетесь сопоставить их с движком выражений с возвратом, это займет значительно больше времени, а время выполнения будет примерно удваиваться для каждого дополнительного выражения перед .aaaaaaaaaaaaaaaaaaaaaaaa!
a
!
Также возможно обратное отслеживание, которое является полиномиальным временем , а не экспоненциальным. Это также может вызвать проблемы для достаточно длинных входных данных, хотя этой проблеме уделялось меньше внимания, поскольку вредоносный входной сигнал должен быть намного длиннее, чтобы иметь значительный эффект. Примером такого шаблона является " ", когда входные данные представляют собой произвольно длинную последовательность " ".a*b?a*x
a
Так называемые "злые" или уязвимые регулярные выражения были обнаружены в онлайн-репозиториях регулярных выражений. Обратите внимание, что достаточно найти уязвимое подвыражение , чтобы атаковать полное регулярное выражение:
^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$
^(([a-z])+.)+[A-Z]([a-z])+$
Эти два примера также восприимчивы к входным данным aaaaaaaaaaaaaaaaaaaaaaaa!
.
Если само регулярное выражение подвержено влиянию пользовательского ввода, например, веб-сервиса, разрешающего клиентам предоставлять шаблон поиска, то злоумышленник может внедрить вредоносное регулярное выражение для потребления ресурсов сервера. Поэтому в большинстве случаев отказ в обслуживании регулярных выражений можно избежать, исключив возможность для пользователя выполнять произвольные шаблоны на сервере. В этом случае веб-приложения и базы данных являются основными уязвимыми приложениями. В качестве альтернативы вредоносная страница может повесить веб-браузер пользователя или заставить его использовать произвольные объемы памяти.
Однако если уязвимое регулярное выражение уже существует на стороне сервера, то злоумышленник может вместо этого предоставить входные данные, которые запускают его худшее поведение. В этом случае сканеры электронной почты и системы обнаружения вторжений также могут быть уязвимы.
В случае веб-приложения программист может использовать одно и то же регулярное выражение для проверки ввода как на стороне клиента, так и на стороне сервера системы. Злоумышленник может проверить клиентский код, ища вредоносные регулярные выражения, и отправить созданный ввод непосредственно на веб-сервер, чтобы повесить его. [9]
ReDoS можно смягчить без внесения изменений в механизм регулярных выражений, просто установив ограничение по времени для выполнения регулярных выражений при использовании ненадежных входных данных. [10]
ReDoS можно полностью избежать, используя неуязвимую реализацию регулярных выражений. После того, как брандмауэр веб-приложений CloudFlare (WAF) был выведен из строя PCRE ReDoS в 2019 году, компания переписала свой WAF для использования библиотеки регулярных выражений Rust без возврата, используя алгоритм, аналогичный RE2 . [11] [12]
Уязвимые регулярные выражения могут быть обнаружены программно с помощью линтера . [13] Методы варьируются от чисто статического анализа [14] [15] до фаззинга . [16] В большинстве случаев проблемные регулярные выражения могут быть переписаны как «незлые» шаблоны. Например, (.*a)+
можно переписать в ([^a]*a)+
. Притяжательное соответствие и атомарная группировка , которые отключают возврат для частей выражения, [17] также могут использоваться для «успокоения» уязвимых частей. [18] [19]
{{cite web}}
: Внешняя ссылка в |author=
( помощь )CS1 maint: числовые имена: список авторов ( ссылка )При использовании System.Text.RegularExpressions для обработки ненадежных входных данных передайте тайм-аут. Злонамеренный пользователь может предоставить входные данные RegularExpressions, что приведет к атаке типа "отказ в обслуживании". API-интерфейсы фреймворка ASP.NET Core, использующие RegularExpressions, передают тайм-аут.