preg_replace — 执行正则表达式的搜索和替换
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit ] )
在 subject 中搜索 pattern 模式的匹配项并替换为 replacement 。如果指定了 limit ,则仅替换 limit 个匹配,如果省略 limit 或者其值为 -1,则所有的匹配项都会被替换。
特别注意:
/e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。提示:要确保 replacement 构成一个合法的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错误。
在Discuz程序中很聪明的利用了这个“e”,比如说在生成模板缓冲的时候,部分php代码执行了,而部分php代码未执行。如下代码:
$template = preg_replace("/([\n\r]+)\t+/s", "\\1", $template);
$template = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $template);
$template = preg_replace("/\{lang\s+(.+?)\}/ies", "\$this->languagevar('\\1')", $template);
$template = preg_replace("/[\n\r\t]*\{block\/(\d+?)\}[\n\r\t]*/ie", "\$this->blocktags('\\1')", $template);
可以看出文字替换就是提前处理了。
上面的这种写法完全可以用下面的方式,显得更加易读易懂
$template = preg_replace("/\{lang\s+(.+?)\}/is", $this->languagevar('\\1'), $template);
在有这些好处的同时,这个"e"往往在不注意的情况下很容易产生漏洞,还请慎用。下面举例说明:
比如说某程序文件中有这样的写法
<?php
function test($str){
//......
//......
return $str;
}
echo preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", 'test("\1")', $_GET["h"]);
?>
提交 ?h=[php]phpinfo()[/php],phpinfo()会被执行吗?
肯定不会。
因为经过正则匹配后, replacement 参数变为'test("phpinfo()")',此时phpinfo仅是被当做一个字符串参数了。
有没有办法让它执行呢?
当然有。
在这里我们如果提交?h=[php]{${phpinfo()}}[/php],phpinfo()就会被执行。
为什么呢?
在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。
注意:双引号中的函数不会被执行和替换。
在这里我们需要通过{${}}构造出了一个特殊的变量,'test("{${phpinfo()}}")',达到让函数被执行的效果 ${phpinfo()} 会被解释执行。
phpinfo() 可以获取到服务器端的各种信息,方便进一步攻击,所以这是每个网站基本应该预防的一点。
如何避免出现上面的漏洞呢?
可以把上面的代码这样写,用户就没有办法攻击了
echo preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", "test('\\1')", $_GET["h"]);
上面的内容仅供学习参考,并无任何恶意。
上面的程序还有一点点小的区别哦,看下面两行代码对比
echo preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", "test('\1')", $_GET["h"]);
//下面两行代码替换是等价的,注意单引号与双引号
echo preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", "test('\\1')", $_GET["h"]);
echo preg_replace("/\s*\[php\](.+?)\[\/php\]\s*/ies", 'test("\1")', $_GET["h"]);