Может ли кто-нибудь объяснить модификатор / e regex?

В настоящее время я улучшаю свои знания о дырах в области безопасности в HTML, PHP, JavaScript и т. Д. Несколько часов назад я наткнулся на модификатор /e в регулярных выражениях, и я до сих пор не понимаю, как это работает. Я взглянул на документацию, но это действительно не помогло.

Я понял, что этот модификатор можно манипулировать, чтобы дать кому-то возможность выполнить PHP-код (например, preg_replace() ). Я видел следующий пример, описывающий дыру в безопасности, но это не объяснялось, так что может кто-нибудь объяснить мне, как вызвать phpinfo() в следующем коде?

 $input = htmlentities(""); if (strpos($input, 'bla')) { echo preg_replace("/" .$input ."/", $input ."<img src='".$input.".png'>", "bla"); } 

Модификатор e Regex в PHP с примером уязвимости и альтернативы

Что e , с примером …

Модификатор eустаревший модификатор регулярных выражений, который позволяет использовать PHP-код в вашем регулярном выражении. Это означает, что все, что вы анализируете, будет оцениваться как часть вашей программы.

Например, мы можем использовать что-то вроде этого:

 $input = "Bet you want a BMW."; echo preg_replace("/([az]*)/e", "strtoupper('\\1')", $input); 

Это приведет к выпуску BET YOU WANT A BMW.

Без e модификатора мы получаем этот отличный результат:

 strtoupper('')Bstrtoupper('et')strtoupper('') strtoupper('you')strtoupper('') strtoupper('want')strtoupper('') strtoupper('a')strtoupper('') strtoupper('')Bstrtoupper('')Mstrtoupper('')Wstrtoupper('').strtoupper('') 

Потенциальные проблемы безопасности с e

Модификатор e устарел из соображений безопасности . Вот пример проблемы, с которой вы можете столкнуться очень легко с помощью e :

 $password = 'secret'; ... $input = $_GET['input']; echo preg_replace('|^(.*)$|e', '"\1"', $input); 

Если я отправлю свой ввод как "$password" , вывод этой функции будет secret ( demo ). Поэтому для меня очень легко получить доступ к переменным сеанса, причем все переменные используются в фоновом режиме и даже принимают более глубокие уровни контроля над вашим приложением ( eval('cat /etc/passwd'); ) через эту простую часть плохо написанного кода.

Подобно аналогично устаревшим библиотекам mysql , это не означает, что вы не можете писать код, который не подвержен уязвимости с помощью e , просто это сложнее сделать.

Что вы должны использовать вместо этого …

Вы должны использовать preg_replace_callback почти во всех местах, которые вы могли бы использовать с помощью модификатора e . В этом случае код определенно не так краток, но не позволяйте этому обмануть вас – он в два раза быстрее:

 $input = "Bet you want a BMW."; echo preg_replace_callback( "/([az]*)/", function($matches){ foreach($matches as $match){ return strtoupper($match); } }, $input ); 

По производительности, нет оснований для использования e

В отличие от библиотек mysql (которые также были устаревшими для целей безопасности), e не быстрее, чем его альтернативы для большинства операций. В приведенном примере он в два раза медленнее: preg_replace_callback (0,14 с для 50 000 операций) против модификатора (0,32 с для 50 000 операций)

Модификатор e является модификатором, специфичным для PHP, который запускает PHP для запуска результирующей строки в виде кода PHP. Это в основном eval() завернутый внутри движка регулярных выражений.

eval() по себе считается угрозой безопасности и проблемой производительности; обертывание его внутри регулярного выражения значительно усиливает эти проблемы.

Поэтому он считается плохой практикой и официально считается устаревшим из скоро появляющегося PHP v5.5.

PHP предоставил несколько версий теперь альтернативное решение в виде preg_replace_callback() , которое использует функции обратного вызова вместо использования eval() . Это рекомендуемый метод такого рода вещей.

С учетом кода, который вы указали:

Я не вижу модификатора e в примере кода, который вы указали в вопросе. Он имеет косую черту на каждом конце как разделитель регулярных выражений; e должно быть вне этого, и это не так. Поэтому я не думаю, что код, который вы цитировали, скорее всего, будет непосредственно уязвим для того, чтобы в него был добавлен e модификатор.

Однако, если $input содержит любые / символы, он будет уязвим для того, чтобы быть полностью разбитым (т. Е. Выбрасывать ошибку из-за недопустимого регулярного выражения). То же самое было бы применимо, если бы у него было что-то еще, что сделало бы это неправильным регулярным выражением.

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

Как поясняется в руководстве, модификатор /e фактически оценивает текст, в котором регулярное выражение работает как PHP-код . Пример, приведенный в руководстве:

 $html = preg_replace( '(<h([1-6])>(.*?)</h\1>)e', '"<h$1>" . strtoupper("$2") . "</h$1>"', $html ); 

Это соответствует любому <hX>XXXXX</hX> « <hX>XXXXX</hX> » (т. <hX>XXXXX</hX> Заголовкам HTML-тегов), заменяет этот текст "<hX>" . strtoupper("XXXXXX") . "<hX>" "<hX>" . strtoupper("XXXXXX") . "<hX>" "<hX>" . strtoupper("XXXXXX") . "<hX>" , затем выполняет "<hX>" . strtoupper("XXXXXX") . "<hX>" "<hX>" . strtoupper("XXXXXX") . "<hX>" "<hX>" . strtoupper("XXXXXX") . "<hX>" как PHP-код, затем возвращает результат в строку.

Если вы запускаете это при произвольном вводе пользователя , у любого пользователя есть шанс проскользнуть что-то, в котором на самом деле будет оцениваться PHP-код. Если он делает это правильно, пользователь может использовать эту возможность для выполнения любого кода, который он хочет. В приведенном выше примере предположите, что на втором этапе текст будет "<hX>" . strtoupper("" . shell('rm -rf /') . "") . "<hX>" "<hX>" . strtoupper("" . shell('rm -rf /') . "") . "<hX>" "<hX>" . strtoupper("" . shell('rm -rf /') . "") . "<hX>" .

Это зло, вот и все, что вам нужно знать: p

Более конкретно, он генерирует заменяющую строку как обычно, но затем запускает ее через eval .

preg_replace_callback этого вы должны использовать preg_replace_callback .