Просматривая StackOverflow и читая отчеты на HackerOne, я все время замечаю, что разработчикам нравятся регулярные выражения. Но они, кажется, их не понимают. Как будто каждый раз, когда строку нужно проанализировать или проверить, они сразу же думают: «О, я знаю, я просто использую регулярное выражение!». Нет. Пожалуйста, не надо. Скорее всего, для этой работы есть гораздо лучший инструмент. Регулярные выражения - не универсальное решение ваших проблем. На самом деле скорее всего они и являются причиной ваших проблем! Но хватит вступления, давайте покажем несколько примеров.

Правила паролей

Люди, которые меня знают, знают, что я страстно ненавижу правила паролей. Особенно те, которые нелепы и действительно ухудшают безопасность. Например, обеспечение максимальной длины. Единственное, что хуже политики плохих паролей, - это ужасные сообщения проверки. Например, ваш пароль отстой и не соответствует нашим критериям. Не поймите меня неправильно. Люблю головоломки и загадки. Но пытаться выяснить ваши непонятные правила паролей во время регистрации - неподходящее время и место. Если только вы не хотите снизить коэффициент конверсии, и в этом случае вы не могли бы выбрать лучшее время. Во всяком случае, вот в чем дело. Часто я вижу, что правила паролей выполняются одним регулярным выражением. Вы только посмотрите на это чудовище (на самом деле оно совсем не чудовищно):

(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z]{8,}$/)

И это главный ответ. Это как если бы люди проголосовали за него, потому что «это так здорово, что тебе удалось собрать все в одном регулярном выражении. Ты намного умнее меня ». Что происходит дальше, вы используете его в продакшене. И проверка вашего пароля сводится к ответу да / нет. У вас не может быть значимых проверочных сообщений, таких как «ваш пароль должен быть длиннее 8 символов (в настоящее время он составляет 7 символов)» , потому что эта информация утеряна. Отдельные регулярные выражения - неправильный инструмент. Почему бы не создать несколько блоков if / else, по одному для каждого критерия? Или декларативный файл конфигурации и простой цикл? Теперь ваш код и правила самодокументируются, и вы получаете содержательные сообщения проверки! # победа

Разбор и проверка URL

Допустим, вы реализовали API postMessage. И, конечно же, вы не забыли проверить свойство event.origin, чтобы сообщения от злоумышленников отклонялись. Большой! Ждать. Что ты сказал? Вы использовали регулярные выражения для извлечения имени хоста из event.origin?

URL.match(/^(.*:\/\/[^\/]+\/.*)@/);

Или вы поместили все принятые источники в сложное регулярное выражение и протестировали с ним event.origin?

new RegExp("^https?://((www\.|encrypted\.)?google(\.com|\.co)?\.[a-z]{2,3}/(search|webhp)\?|24e12c4a-a-95274a9c-s-sites.googlegroups.com/a/google.com/flash-api-test-harness/apiharness.swf|www\.gstatic\.com/doubleclick/studio/innovation/h5/layouts/tetris|tpc\.googlesyndication\.com/safeframe/|lightbox-(demos|builder)\.appspot\.com/|([A-Za-z0-9-]{1,63}\.)*(imasdk\.googleapis\.com|corp\.google\.com|borg\.google\.com|docs\.google\.com|drive\.google\.com|googleads\.g\.doubleclick\.net|googleplex\.com|play\.google\.com|prod\.google\.com|sandbox\.google\.com|photos\.google\.com|picasaweb\.google\.com|lh2\.google\.com|plus\.google\.com|books\.googleusercontent\.com|mail\.google\.com|talkgadget\.google\.com|survey\.g\.doubleclick\.net|youtube\.com|youtube\.googleapis\.com|youtube-nocookie\.com|youtubeeducation\.com|vevo\.com)(:[0-9]+)?([\/\?\#]|$))")

Пожалуйста, не надо. Парсинг URL - это работа, на которую браузеры в полной мере способны. Понимаете, это их работа. Может показаться заманчивым просто написать одно регулярное выражение и покончить с работой. Но сможете ли вы понять регулярное выражение через неделю? А как насчет других, кто работает над проектом? Что, если вам нужно добавить еще один URL, вы просто расширите регулярное выражение и скрестите пальцы? Не надо. Используйте подходящую библиотеку синтаксического анализа URL, создайте белый список имен хостов (и путей, если необходимо). Сделайте так, чтобы код говорил сам за себя.

Разбор языков более высокого уровня (HTML, CSS и т. Д.)

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

Проще говоря, в нем говорится, что регулярные грамматики (регулярные выражения) менее эффективны, чем контекстно-свободные грамматики (например, HTML). Другими словами, вы никогда не сможете анализировать произвольный HTML с помощью одного регулярного выражения по определению (обязательно https://stackoverflow.com/a/1732454/1422124 ). Но по-прежнему каждый день разработчики пытаются делать именно это, потому что они просто не знают лучшего. Не думаю, что у меня когда-нибудь закончатся примеры. Ознакомьтесь с документацией PHP для strip_tags. Он говорит, что он сломан и не должен использоваться. И верхний комментарий предлагает альтернативу (с использованием регулярных выражений), которая также полностью не работает. Несколько простых примеров, которые можно найти:

//It does not strip self closing tags (expects a closing tag).
strip_tags_content('<img src=x onerror=alert(1)>', '<b>');
-> '<img src=x onerror=alert(1)>'
//It expects 100% well formed HTML. Browsers are lax.
strip_tags_content('<<i>>italic</<i>>', '<b>');
-> '<<i>>italic</<i>>'
//It expects the closing tag to only contain the exact name.
strip_tags_content('<script>alert(1)</script >', '<b>');
-> '<script>alert(1)</script >'
//It has no concept of attributes and removes the value.
strip_tags_content('<b title="<i>test</i>"></b>', '<b>');
-> '<b title=""></b>'

Особенно последний пример демонстрирует сложность синтаксического анализа HTML. Атрибут в кавычках может содержать весь HTML-документ (подумайте, iframe srcdoc), но это все равно просто атрибут в контексте HTML. Решения на основе регулярных выражений обычно рассматривают их как HTML, поскольку они фактически не понимают грамматику HTML.

Вот еще одна жемчужина.

Нет причин не оставлять ValidateRequest включенным. Вы можете отключить его, но у вас должна быть очень веская причина; одним из которых может быть требование пользователя о возможности разместить некоторый HTML-код на сайте для получения лучших параметров форматирования. В этом случае следует ограничить количество разрешенных тегов HTML (‹pre›, ‹b›, ‹i›, ‹P›, ‹br›, ‹hr›) и напишите регулярное выражение, гарантирующее, что ничто другое не будет разрешено или принято.

Позвольте мне повторить важную часть

и напишите регулярное выражение, которое гарантирует, что больше ничего не разрешено и не принято

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

Я знаю, что эти примеры довольно старые (2008 и 2005 гг.). Но иерархия Хомского была описана в 1956 году. И эти источники все еще существуют. Надеюсь, вы почувствовали сложность синтаксического анализа произвольных языков с помощью регулярных выражений. Существуют инструменты, которые могут правильно анализировать HTML, CSS, JavaScript или произвольные языки. В зависимости от ваших требований есть также инструменты более высокого уровня, которые могут просто делать именно то, что вам нужно.

Заключение

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