PHP - это безопасный способ разрешить пользовательские регулярные выражения

Я хотел бы разрешить отправлять для тестирования небольшие пользовательские регулярные выражения. Тем не менее, есть много проблем, которые нужно учитывать, от использования убегающего сервера до большего зла eval() использование.

Насколько мне известно, я решил все проблемы, о которых мог подумать в следующем коде. Есть ли у них какие-нибудь векторы атаки, о которых я не думал? (Довольно наивный вопрос, который я знаю)

function testRegex($regex)
{
    // null character allows a premature regex end and "/../e" injection
    if (strpos($regex, 0) !== false || ! trim($regex)) {
        return false;
    }

    $backtrack_limit = ini_set('pcre.backtrack_limit', 200);
    $recursion_limit = ini_set('pcre.recursion_limit', 20);

    $valid = @preg_match("~$regex~u", null) !== false;

    ini_set('pcre.backtrack_limit', $backtrack_limit);
    ini_set('pcre.recursion_limit', $recursion_limit);

    return $valid;
}


$regexes = array(
    "InvalidRegular)Expression",
    '',
    '\w+',
    '\/\w+/',
    'foo[bar]*',
    '\/\x00known/e' . chr(0x00) . chr(0),
    'known~e' . chr(0),
    'known~e' . chr(0x00),
    '[a-z]+',
    '\p{Lu}+',
);


foreach($regexes as $regex) {
    var_dump($regex, testRegex($regex));
}

Если вы хотите увидеть пример null-byte инъекции:

$user_regex = '.+~e' . chr(0);
$user_match = 'system("whoami")';

var_dump(preg_replace("~$user_regex~u", $user_match, 'foo'));

2 ответа

Решение

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

Добавленная нулевая защита фактически не нужна с 5.4, потому что уже есть проверки в лидере, середине и конце. Последний, в частности, является относительно недавним коммитом (2011) для исправления этой ошибки.

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

Тем не менее, это конкретное решение не дает возможности использовать такие модификаторы, как /s, /i а также /m; возможно, это не ваша главная забота в данный момент, а скорее пища для размышлений:)

Вы можете сказать им, чтобы ввести что-то вроде {выражение}, а затем использовать preg_replace(). Таким образом, они используют только то, что вы им позволяете.

Другие вопросы по тегам