20

我目前正在提高对 HTML、PHP、JavaScript 等安全漏洞的了解。几个小时前,我偶然发现了/e正则表达式中的修饰符,但我仍然不明白它是如何工作的。我已经查看了文档,但这并没有真正帮助。

我的理解是,可以操纵这个修饰符,让某人有机会在(例如,preg_replace())中执行 PHP 代码。我已经看到以下示例描述了一个安全漏洞,但没有解释,所以有人可以解释一下如何phpinfo()在下面的代码中调用吗?

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

4 回答 4

51

PHP 中的正e则表达式修饰符,带有示例漏洞和替代方案

是什么e意思?

e修饰符是一个已弃用的正则表达式修饰符,它允许您在正则表达式中使用 PHP 代码。这意味着您解析的任何内容都将作为程序的一部分进行评估。

例如,我们可以使用这样的东西:

$input = "Bet you want a BMW.";
echo preg_replace("/([a-z]*)/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。因此,对我来说访问会话变量非常容易,所有变量都在后端使用,甚至可以eval('cat /etc/passwd');通过这段简单的写得不好的代码对您的应用程序进行更深层次的控制(?)。

与类似被弃用的mysql库一样,这并不意味着您不能使用 编写不受漏洞影响的代码e,只是这样做更难。

你应该改用什么......

您应该在几乎所有您会考虑使用修饰符的地方使用preg_replace_callback 。e在这种情况下,代码绝对没有那么简短,但不要让它愚弄你——它的速度是原来的两倍:

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

在性能方面,没有理由使用e...

mysql库(出于安全目的也已弃用)不同,e对于大多数操作来说,它并不比它的替代品快。对于给出的示例,它的速度是原来的两倍:preg_replace_callback(50,000 次操作为 0.14 秒)vs e 修改器(50,000 次操作为 0.32 秒)

于 2013-06-07T14:28:33.677 回答
5

e修饰符是 PHP 特定的修饰符,它触发 PHP 将生成的字符串作为 PHP 代码运行。它基本上是一个eval()包裹在正则表达式引擎中的东西。

eval()其本身被视为安全风险和性能问题;将其包装在正则表达式中会显着放大这两个问题。

因此,它被认为是不好的做法,并且从即将发布的 PHP v5.5 开始正式弃用。

PHP 现在已经为几个版本提供了一种替代解决方案preg_replace_callback(),它使用回调函数而不是使用eval(). 这是做这种事情的推荐方法。

具体到您引用的代码:

e我在您在问题中给出的示例代码中没有看到修饰符。它在每端都有一个斜杠作为正则表达式分隔符;thee必须在此之外,但事实并非如此。因此,我认为您引用的代码可能不会直接受到e注入修饰符的影响。

但是,如果$input包含任何/字符,它将很容易被完全破坏(即由于无效的正则表达式而引发错误)。如果它有任何其他使它成为无效正则表达式的东西,这同样适用。

因此,使用未经验证的用户输入字符串作为正则表达式模式的一部分是一个坏主意 - 即使您确定使用e修饰符不会被破解,还有很多其他的恶作剧可以通过它。

于 2013-06-07T14:23:15.017 回答
2

这是邪恶的,这就是你需要知道的一切:p

更具体地说,它正常生成替换字符串,然后通过eval.

你应该preg_replace_callback改用。

于 2013-06-07T14:20:47.953 回答
2

如手册中所述,/e修饰符实际上将正则表达式处理的文本作为 PHP 代码计算。手册中给出的例子是:

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

这匹配任何 " <hX>XXXXX</hX>" 文本(即标题 HTML 标记),用 替换此文本"<hX>" . strtoupper("XXXXXX") . "<hX>",然后作为 PHP 代码执行 "<hX>" . strtoupper("XXXXXX") . "<hX>",然后将结果放回字符串中。

如果您在任意用户输入上运行它,任何用户都有机会滑入实际上将被评估为 PHP 代码的内容。如果他做得正确,用户可以利用这个机会执行他想要的任何代码。在上面的例子中,想象如果在第二步中文本是"<hX>" . strtoupper("" . shell('rm -rf /') . "") . "<hX>".

于 2013-06-07T14:24:43.050 回答