如何在PHP中安全地使用eval函数?

4
今日免费次数已满, 请开通会员/明日再来
$version = $_REQUEST['version'];
$test = 'return $version > 3;';
$success = eval($test);

这显然是一个简化的情况,但有没有什么用户可以输入作为version来执行恶意操作?如果我限制了$test可以采用的字符串类型,将某些变量的值与其他变量进行比较,是否有任何人可以看到利用这一点的方法? 编辑 我已经尝试在服务器上运行以下脚本,但没有任何反应:
<?php
  $version = "exec('mkdir test') + 4";
  $teststr = '$version > 3;';
  $result = eval('return ' . $teststr);
  var_dump($result);
?>

我只得到了bool(false)。没有创建新目录。如果我在这之前有一行实际调用exec('mkdir test')的代码,它就会真正地创建目录。看起来它的工作方式是正确的,它只是将一个转换为数字的字符串与另一个数字进行比较,并发现结果为false。


如下所述,在 PHP 中,$success = eval($test) 的效果与 JavaScript 不同。您需要在 evaled 字符串中进行赋值操作。 - Pekka
1
两件事情:你完全信任用户输入。不仅如此,你还允许用户执行任意代码。瞧,立即就会被攻击利用了!(是的,我会说“永远不要使用eval”) - Piskvor left the building
我尝试在那里注入代码,但对我来说根本不起作用。 - Ed Marty
我不确定我理解你的问题的意义。你是在试图“证明”什么吗?你想要建立什么? - theking2
6个回答

3

哦,天哪!

$version = "exec('rm-rf/...') + 4"; // Return 4 so the return value is "true" 
                                    // after all, we're gentlemen!
$test = "return $version > 3"; 
eval($test);

:)

在这种情况下,您需要对输入值进行至少filter_var()is_numeric()的处理。

顺便说一下,在PHP中使用eval的方式(将其结果分配给$success)是无效的。您需要将分配放入eval()字符串中。


至少$success赋值有效,因为我在eval语句内返回了该值。 - Ed Marty
@Ed 我改口了,从 eval 语句中确实可以“返回”一些东西。你的代码没问题。 - Pekka
关于“exec”这个东西,我尝试写了一个简单的测试,注入了一个“exec”,但它什么也没做。我尝试了一个简单的“exec('mkdir test')”。有任何想法吗? - Ed Marty
我现在认为我看到问题所在了:在$test周围使用双引号。如果我不使用双引号,或者如果我从文件中加载test的值,则不会事先解析$version。如果我不使用双引号,有没有办法妥协系统? - Ed Marty

2

没问题,我会使用$version = (int)$_REQUEST['version'];来验证数据。


2

如果你这样做,只接受整数。

如果必须接受字符串,请不要这样做。

如果你仍然认为必须这样做,请不要这样做!

最后,如果你仍然认为需要字符串,请千万不要这样做!


3
针对评论中说你可以保护字符串的观点,是的,你可以。但前提是你真的知道你在做什么,而且非常非常非常地了解。而原帖的作者并不知道,这并不是针对他个人,也不是针对我或者很可能也不是针对你。 - DampeS8N
请看我上面编辑过的评论。我找不到任何在我的示例中注入代码的字符串。它是否存在任何不安全因素? - Ed Marty
仅仅因为你找不到某些东西,不代表攻击者就找不到。看起来你没有理解我的观点。而且,你已经不再对用户输入使用eval函数了。 - DampeS8N
我认为你也误解了我的观点。无论$version的值是什么,它都被视为字符串并与数字3进行比较。在任何情况下都不会被计算,这不是正确的吗? - Ed Marty
这就是我上次说的。但是通过这样做,它与根本不使用 eval 有什么区别呢?你在一个静态字符串上使用了 eval。例如:$result = $_REQUEST['version'] > 3; 这样做可以达到相同的效果,而无需使用 eval。 - DampeS8N
显示剩余6条评论

1
你需要对“恶意”或“安全”这些定义更加精确。例如,可以考虑以下情况:
exec("rm -rf /");

echo "enlarge your rolex!";

while(true) echo "*";

从常识角度来看,这三个片段都是“恶意”的,但从技术上讲,它们完全不同。适用于#1的保护技术,在其他两个方面不起作用,反之亦然。


0
让这个操作更安全的方法是确保在尝试执行 eval 之前,$version 是一个数字。

-2
使用以下代码仅保留数字(0-9):preg_replace('/[^0-9]+/', '', $version);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接