什么时候是PHP的eval邪恶?

Pie*_*ing 82 php eval

在我用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).';');
Run Code Online (Sandbox Code Playgroud)

Mic*_*cki 129

我会谨慎地调用eval()纯粹的邪恶.动态评估是一种强大的工具,有时可以节省生命.使用eval()可以解决PHP的缺点(见下文).

eval()的主要问题是:

  • 潜在的不安全输入.传递不受信任的参数是一种失败的方法.确保参数(或其一部分)完全受信任通常不是一项微不足道的任务.
  • Trickiness.使用eval()可以使代码更聪明,因此更难以遵循.引用Brian Kernighan" 调试的速度是编写代码的两倍.因此,如果您尽可能巧妙地编写代码,那么根据定义,您不够聪明,无法对其进行调试 "

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

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

根据经验,我倾向于遵循这个:

  1. 有时eval()是唯一/正确的解决方案.
  2. 对于大多数情况,人们应该尝试别的.
  3. 如果不确定,请转到2.
  4. 否则,要非常非常小心.

  • 我不确定它是否算作eval,但是$ result = call_user_func(array('Foo','bar')); 奇迹般有效. (30认同)
  • 这可以被重构以避免eval(),特别是如果$ className被列入白名单,它必须是为了娱乐使用eval().但好消息是,从5.3开始,$ foo :: bar()是有效的. (4认同)
  • 关于棘手的好地方-在您的(简单)示例中,变量“从无处弹出”。如果代码变得稍微复杂一点,那么请下一个查看代码并尝试将变量追逐的人(在那儿,做了那件事,我只不过是一件令人头疼的事情,这是一件糟糕的T恤),祝您好运。 (3认同)

Pat*_*sen 40

当只有最轻微的可能性,即在评估的字符串中包含userinput时,eval是邪恶的.如果您在没有来自用户的内容的情况下进行评估,那么您应该是安全的.

尽管如此,在使用eval之前你应该至少考虑两次,它看起来很简单,但是考虑到错误处理(参见VBAssassins注释),可调试性等等,它不再那么简单了.

所以作为一个经验法则:忘了它.当eval是答案时,你可以提出错误的问题!;-)

  • 我真的很想看到只有eval才是答案的情况. (19认同)
  • 我并不真的认为任何这些都是eval()的合理使用.在每种情况下,不使用eval()做同样的事情仍然是至少可能的.它不像PHP在没有eval()的情况下瘫痪.当然,在这些情况下,eval()是一种快捷方式,但它仍然使您的代码路径更难以遵循,而且问题稍微难以调试.这是我的意见. (8认同)
  • 有时eval就是答案.我们致力于在线游戏应用程序,由于实体之间的关系非常复杂,因此很难避免在那里进行攻击......但在90%的情况下,恕我直言eval不是答案. (6认同)
  • @Christian:你应该先写一个例子(使用[pastebin.com](http://pastebin.com/),并在此处粘贴链接),你认为避免使用`eval()`是不可能的,这个是一种更好的方法.许多人说`eval()`在某些情况下是不可避免的,但他们没有提到任何我们可以论证的具体例子 - 这种辩论方式没有意义.**那么,那些说"eval()"的人是不可避免的,先证明这一点!** (4认同)
  • @Ionut G. Stan数据库存储的对象/实体的自定义触发器? (2认同)
  • @Thomastrutter - 请突出显示一个用表达式解析器代替 eval 的例子。当然,将表达式写入文件并包含它不算数!!;-) (2认同)

tho*_*ter 18

eval()在任何时候都同样邪恶.

"什么时候eval()不邪恶?" 在我看来,这是一个错误的问题,因为它似乎暗示使用eval()的缺点在某些情况下神奇地消失了.

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

此外,没有必要使用eval()的情况 - PHP是一种没有它的全功能编程语言.

无论你是否真的将这些视为邪恶,或者在某些情况下你可以亲自证明使用eval()取决于你.对某些人来说,邪恶太大了,无法证明这一点,而对于其他人来说,eval()是一个方便的捷径.

但是,如果你看到eval()是邪恶的,它在任何时候都是邪恶的.根据具体情况,它并没有神奇地失去它的邪恶.

  • 它添加了另一个攻击向量 - 然后SQL注入也可以在Web服务器上运行任意PHP代码.它需要使用eval().它无法通过字节码缓存进行优化.它违反了分离代码和数据的原则.它可以使您的应用程序不那么便携 - 更新/修复您必须更新数据库的PHP. (11认同)
  • 我要说的是,将PHP代码存储在数据库中同样是邪恶的,同样不必要.无论您想要实现什么,除了将PHP存储在数据库中或使用eval()之外,还有其他方法可以实现.如果你愿意这样做,如果它对你来说是一个有用的快捷方式,但是如果你把eval()视为邪恶,那么你可能不得不将数据库中的PHP存储视为邪恶. (3认同)
  • 你真是个程序员。 (2认同)
  • 我要说的是,如果只能通过使用eval才能实现某些目标,那么重新考虑这样做是一件好事。在运行时动态创建类似乎是个坏主意。为什么不使用一些代码生成并提前生成定义它们的代码? (2认同)

Bla*_*ura 14

在这种情况下,eval可能足够安全,只要用户不可能在表中创建任意列.

但它并不是那么优雅.这基本上是一个文本解析问题,滥用PHP的解析器处理似乎有点hacky.如果您想滥用语言功能,为什么不滥用JSON解析器?至少使用JSON解析器,完全没有代码注入的可能性.

$json = str_replace(array(
    'enum', '(', ')', "'"), array)
    '',     '[', ']', "'"), $type);
$result = json_decode($json);
Run Code Online (Sandbox Code Playgroud)

正则表达式可能是最明显的方式.您可以使用单个正则表达式从此字符串中提取所有值:

$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];
Run Code Online (Sandbox Code Playgroud)


Gre*_*nie 11

当您在eval中使用外部数据(例如用户输入)时.

在上面的示例中,这不是问题.


Ali*_*xel 11

eval() 很慢,但我不会称之为邪恶.

这是我们对它的不良用处,可能会导致代码注入并且是邪恶的.

一个简单的例子:

$_GET = 'echo 5 + 5 * 2;';
eval($_GET); // 15
Run Code Online (Sandbox Code Playgroud)

一个有害的例子:

$_GET = 'system("reboot");';
eval($_GET); // oops
Run Code Online (Sandbox Code Playgroud)

我建议你不要使用,eval()但如果你这样做,请确保验证/白名单所有输入.


ste*_*efs 7

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

  1. Eval本质上总是一个安全问题.

  2. 除了安全问题,eval还存在速度极慢的问题.在我对PHP 4.3.10的测试中,它比普通代码慢10倍,在PHP 5.1 beta1上慢28倍.

blog.joshuaeichorn.com:using-eval-in-php


Fra*_*ois 6

eval()永远是邪恶的。

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

  • -1,因为它并不总是邪恶的;只是经常。 (9认同)
  • 坦白说,我认为@MichałRudnicki的回答说得最好。不要误会我的意思,每当我问一个问题时,答案就是eval(),我就以为我问错了。很少有“正确”的答案,但总的来说,邪恶总是“邪恶的”,这是不正确的。 (3认同)

Par*_*ase 5

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

我也觉得,由于使用eval的95%(或更多)是积极危险的,因此在其他情况下可能提供的小的潜在时间节省并不值得沉迷于使用它的不良做法.另外,你以后必须向你的小伙子解释为什么你使用eval是好的,而且他们的坏.

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

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

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

还有一个比社交技术更强的技术:

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

在第一种情况下,您将冒险(显然,不是在您评估已知字符串时)执行任意代码.但是,您的输入可能不像您想象的那样已知或固定.

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

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

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

这大致是您的选项所做的,但为了清晰和安全,我会包装一些if和它的注释(例如,如果第一个匹配不匹配,抛出异常或设置null结果).

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

使用preg_version,最糟糕的结果可能是$ result = null,eval版本最差的是未知的,但至少是崩溃.