在我使用 php 进行开发的这些年里,我总是听说使用 eval() 是邪恶的。

考虑以下代码,使用第二个(也是更优雅的)选项不是有意义吗?如果没有,为什么?

// $type is the result of an SQL statement
// e.g. SHOW COLUMNS FROM a_table LIKE 'a_column';
// hence you can be pretty sure about the consistency
// of your string
$type = "enum('a','b','c')";

// possibility one
$type_1 = preg_replace('#^enum\s*\(\s*\'|\'\s*\)\s*$#', '', $type);
$result = preg_split('#\'\s*,\s*\'#', $type_1);

// possibility two
eval('$result = '.preg_replace('#^enum#','array', $type).';');
有帮助吗?

解决方案

我会谨慎地将 eval() 称为纯粹的邪恶。 动态评估是一个强大的工具,有时可以成为救星。使用 eval() 可以解决 PHP 的缺点(见下文)。

eval() 的主要问题是:

  • 潜在的不安全输入。 传递不受信任的参数是一种失败的方式。确保参数(或其一部分)完全可信通常不是一项简单的任务。
  • 狡猾。 使用 eval() 使代码变得更聪明,因此更难以理解。引用布赖恩·克尼根的话“调试的难度是最初编写代码的两倍。因此,如果您尽可能巧妙地编写代码,那么根据定义,您就不够聪明来调试它"

实际使用 eval() 的主要问题只有一个:

  • 缺乏经验的开发人员在没有充分考虑的情况下使用它。

根据经验,我倾向于遵循以下原则:

  1. 有时 eval() 是唯一/正确的解决方案。
  2. 在大多数情况下,人们应该尝试其他方法。
  3. 如果不确定,请转至 2。
  4. 别的, 要非常非常小心。

其他提示

EVAL是恶当仅存在userinput被包括在评估串中的半点可能性。 当你没有从用户处传来的内容EVAL,你应该是安全的。

不过你应该考虑至少两次使用eval之前,它看起来欺骗性简单,但与错误处理(见VBAssassins评论),可调试性等考虑,它不是那么简单了。

因此,作为一个经验法则: 忘掉它。当EVAL是你propably问错了问题的答案! ; - )

<强>的eval()是在所有时间同样恶

“当是的eval()不为恶?”是一个错误的问题是问我的意见,因为它似乎暗示缺点使用eval()奇迹般地在某些情况下消失。

使用eval()通常是一个坏主意,因为它降低了代码的可读性,有能力为您预测的代码路径(和那可能的安全隐患)运行之前,因此有能力调试代码。使用eval()还可以防止所评估的代码和从由一个操作码缓存被优化如在Zend Opcache集成到PHP 5.5及以上,或由JIT编译器,如一个在HHVM包围它的代码。

此外,不存在的情况,这就是它绝对需要使用eval() - PHP是没有它的完全能够编程语言

不管是不是你真正看到这些为邪恶,或者您可以亲自证明在某些情况下使用eval()是你的。一些人认为,罪恶太大有史以来证明它,并给他人,的eval()是一个方便的捷径。

不过,如果你看到的eval()是邪恶的,它是邪恶的时刻。根据上下文它不会奇迹般地失去其恶

在这种情况下,EVAL可能是足够安全的,只要它永远不会有可能在一个表由用户创建任意列。

这不是真的任何更优雅,但。这基本上是一个文本解析问题,以及滥用PHP解析器来手柄似乎有点哈克。如果你想滥用语言功能,为什么不滥用JSON解析器?至少与JSON解析器,有在所有的代码注入的无可能性。

$json = str_replace(array(
    'enum', '(', ')', "'"), array)
    '',     '[', ']', "'"), $type);
$result = json_decode($json);

一个正则表达式可能是最明显的方法。可以使用一个正则表达式,从该字符串中提取所有的值:

$extract_regex = '/
    (?<=,|enum\()   # Match strings that follow either a comma, or the string "enum("...
    \'      # ...then the opening quote mark...
    (.*?)       # ...and capture anything...
    \'      # ...up to the closing quote mark...
    /x';
preg_match_all($extract_regex, $type, $matches);
$result = $matches[1];

当您使用eval内部外国数据(例如用户输入)。

在上面的示例中,这不是一个问题。

eval()是缓慢的,但我不会把它恶

这是不好的我们如何使用它,可导致的代码注入并恶。

一个简单的例子:

$_GET = 'echo 5 + 5 * 2;';
eval($_GET); // 15

一个harmlful示例:

$_GET = 'system("reboot");';
eval($_GET); // oops

我劝你不要使用eval()但如果这样做,一定要验证/白名单的所有输入。

我会公然窃取这里的内容:

  1. 评估本质上始终是一个安全问题。

  2. 除了安全问题之外,eval 还存在速度极其缓慢的问题。在我对 PHP 4.3.10 的测试中,它比正常代码慢 10 倍,在 PHP 5.1 beta1 上慢 28 倍。

博客.joshuaeichorn.com:在 php 中使用 eval

就我个人而言,我认为该代码仍然非常邪恶,因为您没有评论它在做什么。它也没有测试其输入的有效性,这使得它非常脆弱。

我还认为,由于 95%(或更多)的 eval 使用都是非常危险的,因此在其他情况下它可能节省的微小时间并不值得沉迷于使用它的不良做法。另外,你稍后必须向你的下属解释为什么你使用 eval 是好的,而他们的使用是不好的。

当然,您的 PHP 最终看起来像 Perl ;)

eval() 有两个关键问题(作为“注入攻击”场景):

1)可能造成伤害2)可能只是崩溃

还有一种社交性多于技术性的:

3)它会诱使人们在其他地方不恰当地使用它作为捷径

在第一种情况下,您面临任意代码执行的风险(显然,当您评估已知字符串时)。不过,您的输入可能并不像您想象的那样已知或固定。

更有可能(在这种情况下)你会崩溃,并且你的字符串将终止并出现无缘无故的模糊错误消息。恕我直言,所有代码都应该尽可能整齐地失败,否则它应该抛出异常(作为最容易处理的错误形式)。

我建议,在这个例子中,您是通过巧合进行编码,而不是根据行为进行编码。是的,SQL 枚举语句(您确定该字段的枚举吗?- 您是否调用了正确版本的数据库的正确表的正确字段?它真的回答了吗?)恰好看起来像 PHP 中的数组声明语法,但我建议您真正想做的不是找到从输入到输出的最短路径,而是解决指定的任务:

  • 确定您有一个枚举
  • 提取内部列表
  • 解压列表值

这大致就是您的选项的作用,但为了清晰和安全,我会在它周围包含一些 if 和注释(例如,如果第一个匹配项不匹配,则抛出异常或设置 null 结果)。

转义逗号或引号仍然存在一些可能的问题,您可能应该解压数据然后取消引号,但它至少将数据视为数据,而不是代码。

使用 preg_version 最坏的结果可能是 $result=null,使用 eval 版本最坏的结果是未知的,但至少会崩溃。

我还付出一些代价的人维护你的代码。

的eval()不是easiet只是看看,知道什么是应该发生的,你的例子并没有那么糟糕,但在其他地方也可以是一个正确的噩梦。

eval()总是 邪恶的。

  • 出于安全原因
  • 出于性能原因
  • 出于可读性/可重用性原因
  • 由于 IDE/工具原因
  • 出于调试原因
  • 总有更好的方法

评估评估字符串作为密码,用这个问题是如果字符串以任何方式“污点”,它可能使巨大的安全威胁。通常情况下,问题是在用户输入字符串中的评估在很多情况下,用户可以输入代码(PHP或SSI为例),其随后eval里运行,它会以相同的权限运行的PHP脚本,可能的情况下,被用来获取信息/访问您的服务器。它可以是相当棘手,使其递给EVAL之前,请确保用户输入正确清理。还有其他问题...其中一些是有争议的

PHP建议,你写你的代码,以这样一种方式,它可以通过call_user_func改为执行做明确evals的。

另一个原因是eval恶是,它不能由PHP字节码缓存像eAccelertor或ACP缓存。

这是不好的编程,使的eval()邪恶,而不是功能。我有时会使用它,因为我不能左右它得到在多个网站上的动态规划。我不能让PHP在一个站点被解析,因为我不会接受我想要的东西。我只想得到一个结果!我很高兴的功能,与eval()存在,因为它让我的生活变得更加容易。用户输入?唯一不好的程序员们被黑客勾搭上了。我不担心这一点。

  

这是不好的编程,使的eval()邪恶,而不是功能。我有时会使用它,因为我不能左右它得到在多个网站上的动态规划。我不能让PHP在一个站点被解析,因为我不会接受我想要的东西。我只想得到一个结果!我很高兴的功能,与eval()存在,因为它让我的生活变得更加容易。用户输入?唯一不好的程序员们被黑客勾搭上了。我不担心这一点。

我预测你将有严重的问题,很快...

在所有诚实,也绝对对过高的功能如EVAL,在一个解释性语言,如PHP没有很好的利用。我从来没有见过的eval使用其他的,更安全的方式来执行其已经不能执行的程序功能...

评估是一切罪恶的根源,我完全同意,到认为测试用户输入将帮助所有的人。三思而后行,用户的输入可以有许多不同的形式,在我们说话的黑客正在利用该功能你根本不够关心。在我看来,只是避免EVAL干脆。

我见过制作的例子来滥用这种超出了我自己的创意eval函数。从安全的立场,不惜一切代价避免,我甚至会去尽可能要求它是起码的PHP配置的选项,而不是“给定”。

我以前用的eval()很多,但我发现,大多数的你不必用eval做花样的情况。那么,你必须在PHP call_user_func()和call_user_func_array()。这是不够好,静态和动态调用任何方法。

要执行静态呼叫构造回调作为阵列(“CLASS_NAME”,“METHOD_NAME”),或甚至作为简单的字符串,如“CLASS_NAME :: METHOD_NAME”。要执行动态呼叫使用阵列($对象,“方法”)样式的回调。

有关的eval()唯一明智的用途是编写自定义编译器。我做了一个,但EVAL仍然是邪恶的,因为它是如此该死很难调试。最糟糕的事情是evaled代码的致命错误崩溃调用它的代码。我用Parsekit PECL扩展到至少检查语法,但仍然没有喜悦 - 尝试指未知的类和整个应用程序崩溃

下面是运行PHP代码的溶液从数据库中抽取,而无需使用EVAL。允许所有的范围的功能和不同的是:

$rowId=1;  //database row id
$code="echo 'hello'; echo '\nThis is a test\n'; echo date(\"Y-m-d\");"; //php code pulled from database

$func="func{$rowId}";

file_put_contents('/tmp/tempFunction.php',"<?php\nfunction $func() {\n global \$rowId;\n$code\n}\n".chr(63).">");

include '/tmp/tempFunction.php';
call_user_func($func);
unlink ('/tmp/tempFunction.php');

基本上它创建了在文本文件中所包括的代码中的唯一功能,包括文件,调用函数,那么当用它做删除该文件。我用这其中每一步都需要独特的代码来处理工艺来执行日常数据库内容撷取/ syncronizations。这已经解决了所有我所面临的问题。

除安全问题,EVAL()不能被编译,优化或操作码缓存,从而将永诺比较慢 - 的方式慢 - 比正常PHP代码。因此非高性能使用eval,allthough不让它邪恶。 (goto是邪,eval只有不好的做法/臭代码/难看)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top