什么时候在eval邪恶的PHP?
在所有这些年来,我一直在使用PHP进行开发,我一直听说使用eval()
是邪恶的。
考虑下面的代码,使用第二个(更优雅的)选项没有意义吗? 如果没有,为什么?
// $type is the result of an SQL statement // eg 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()纯粹的邪恶。 dynamic评估是一个强大的工具,有时可以成为一个生命的救星。 用eval()可以解决PHP的缺点(见下文)。
eval()的主要问题是:
- 潜在的不安全的input。 传递一个不可信的参数是一种失败的方法。 确保一个参数(或其一部分)是完全可信的往往不是一件容易的事情。
- Trickiness。 使用eval()可以使代码更加灵活,因此更难以遵循。 引用Brian Kernighan“ debugging比编写代码要困难一倍,因此,如果您尽可能巧妙地编写代码,那么根据定义,您的debugging不够聪明 ”
实际使用eval()的主要问题只有一个:
- 没有足够的考虑,没有经验的开发者使用它。
作为一个经验法则,我倾向于遵循这一点:
- 有时候eval()是唯一/正确的解决scheme。
- 对于大多数情况下,应该尝试别的。
- 如果不确定,转到2。
- 否则, 要非常小心。
当评估string中只包含用户input的一点可能性时,eval是邪恶的。 当你没有来自用户的内容评估时,你应该是安全的。
尽pipe如此,在使用eval之前,你应该至less考虑两次,它看起来很简单,但是在error handling(参见VBAssassins评论),可debugging性等方面,并不是那么简单。
所以作为一个经验法则:忘记它。 当eval是答案时,你可以问一个错误的问题! 😉
eval()在任何时候都同样是邪恶的。
“什么时候eval()不是邪恶的?” 在我看来,这是一个错误的问题,因为它似乎意味着使用eval()的缺点在某些情况下会奇迹般地消失。
使用eval()通常是一个坏主意,因为它会降低代码的可读性,在运行时预测代码path(以及可能的安全含义)的能力,从而debugging代码的能力。 使用eval()还可以防止评估的代码及其周围的代码被操作码caching(如集成到PHP 5.5及更高版本的Zend Opcache)或JH编译器(如HHVM中的那个)优化。
此外,没有必要使用eval()的情况 – PHP是没有它的全function编程语言。
不pipe你是否真的把这些看作是罪恶,或者你可以亲自certificate在某些情况下使用eval()取决于你。 对某些人来说,这些罪恶太过分了,以至于无法certificate这一点。对于其他人来说,eval()是一个方便的捷径。
但是,如果你看到eval()是邪恶的,那么它总是邪恶的。 它不会神奇地根据情境失去它的邪恶。
在这种情况下,eval可能足够安全,只要用户在表中创build任意列是不可能的。
虽然这不是更优雅。 这基本上是一个文本parsing问题,并滥用PHP的parsing器来处理似乎有点hacky。 如果你想滥用语言function,为什么不滥用JSONparsing器? 至less在JSONparsing器中,没有任何代码注入的可能性。
$json = str_replace(array( 'enum', '(', ')', "'"), array) '', '[', ']', "'"), $type); $result = json_decode($json);
正则expression式可能是最明显的方法。 您可以使用单个正则expression式从该string提取所有值:
$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中使用外部数据(如用户input)时。
在你上面的例子中,这不是一个问题。
eval()
很慢,但我不会把它称为邪恶的。
我们对它的使用是不好的,可能会导致代码注入和恶意。
一个简单的例子:
$_GET = 'echo 5 + 5 * 2;'; eval($_GET); // 15
一个可笑的例子:
$_GET = 'system("reboot");'; eval($_GET); // oops
我build议你不要使用eval()
但如果你这样做,请确保你validation/白名单的所有input。
我会公然在这里窃取内容:
从其性质来看,总是会成为一个安全问题。
除了安全问题,eval还有一个令人难以置信的慢的问题。 在我的PHP 4.3.10testing中,它比普通代码慢了10倍,在PHP 5.1 beta1上慢了28倍。
blog.joshuaeichorn.com:using-eval-in-php
就我个人而言,我认为这个代码还是非常邪恶的,因为你没有评论它正在做什么。 这也不是testing其input的有效性,使其非常脆弱。
我也觉得,由于eval的95%(或更多)的使用是危险的,所以在其他情况下可能提供的小的时间节省不值得放弃使用它的坏习惯。 另外,你稍后必须向你的奴才解释为什么你使用eval是好的,而他们的不好。
当然,你的PHP最终看起来像Perl;)
eval()有两个关键问题,(作为“注入攻击”场景):
1)可能造成伤害2)可能只是崩溃
还有一个比社交技术更有意义的方法:
3)它会引诱人们不适当地使用它作为其他地方的捷径
在第一种情况下,你冒险(显然,不是当你评估一个已知string时)的任意代码执行。 尽pipe如此,您的input可能不像您所想的那样是已知的或固定的。
更有可能(在这种情况下),你只会崩溃,你的string将终止与无偿晦涩的错误信息。 恕我直言,所有的代码应该尽可能地失败,否则它应该抛出一个exception(作为最容易处理的错误forms)。
我build议,在这个例子中,你是巧合而不是编码的行为编码。 是的,SQL的枚举语句(你确定这个字段的枚举吗? – 你调用正确的数据库版本的正确的表吗?它实际上是否回答?)看起来像PHP中的数组声明语法,但是我build议你真正想要做的不是find从input到输出的最短path,而是解决指定的任务:
- 确定你有一个枚举
- 提取内部列表
- 解压列表值
这大致上是你的select,但我会包装一些if和评论周围清晰和安全(例如,如果第一个匹配不匹配,抛出exception或设置空结果)。
转义逗号或引号仍然存在一些可能的问题,您应该解压缩数据,然后将其解除引号,但至less将数据视为数据而不是代码。
有了preg_version,最糟糕的结果可能是$ result = null,而eval版本最糟糕的是未知的,但至less是一次崩溃。
我也会向维护你的代码的人付出一些代价。
eval()不是一个简单的东西,只要看看,知道应该发生什么,你的例子不是那么糟糕,但在其他地方,它可能是一个正确的噩梦。
eval将string评估为代码,问题是如果string以任何方式被“污染”,它可能会暴露巨大的安全威胁。 通常问题出在用户input在string中被评估的情况下,在很多情况下,用户可以input在eval中运行的代码(例如php或者ssi),它将以与你的php脚本相同的权限运行,并且可以被用来获取信息/访问您的服务器。 在把它交给eval之前,确保用户input已经被正确地清理出来可能是相当棘手的。 还有其他问题,其中一些是有争议的
PHPbuild议您编写代码的方式是通过call_user_func执行,而不是执行明确的执行。
eval()
永远是邪恶的。
- 出于安全原因
- 出于性能原因
- 为了可读性/可重用性的原因
- 为IDE /工具的原因
- 出于debugging的原因
- 总有一个更好的办法
eval
是邪恶的另一个原因是它不能像eAccelertor或ACP那样通过PHP字节码caching来caching。
这是不好的编程,使eval()邪恶,而不是function。 我有时使用它,因为我无法在多个站点上进行dynamic编程。 我不能在一个站点parsingPHP,因为我不会收到我想要的东西。 我只会收到一个结果! 我很高兴eval()存在,因为它让我的生活变得更加简单。 用户input? 只有不好的程序员被黑客迷住了。 我不担心这一点。
我曾经使用eval()很多,但是我发现大多数情况下你不必使用eval来做技巧。 那么,你在PHP中有call_user_func()和call_user_func_array()。 静态和dynamic地调用任何方法已经足够了。
要执行静态调用,请将数组(“class_name”,“method_name”)的callback或像“class_name :: method_name”一样的简单string构造出来。 执行dynamic调用使用数组($ object,'method')样式的callback。
eval()唯一合理的用法是编写一个自定义的编译器。 我做了一个,但eval仍然是邪恶的,因为它是如此难以debugging。 最糟糕的是,代码中的致命错误会导致代码崩溃。 我用Parsekit PECL扩展至less检查语法,但仍然没有喜悦 – 尝试引用未知的类和整个应用程序崩溃。
这是不好的编程,使eval()邪恶,而不是function。 我有时使用它,因为我无法在多个站点上进行dynamic编程。 我不能在一个站点parsingPHP,因为我不会收到我想要的东西。 我只会收到一个结果! 我很高兴eval()存在,因为它让我的生活变得更加简单。 用户input? 只有不好的程序员被黑客迷住了。 我不担心这一点。
我预测你很快就会有严重的问题
诚实地说,像PHP这样的解释型语言中,eval等高级函数绝对没有什么用处。 我从来没有见过使用其他更安全的方法执行程序function的eval程序。
Eval是所有邪恶的根源,我完全同意,所有认为testing用户input将会有所帮助的人们。 仔细想想,用户input可能会有很多不同的forms,而且我们说黑客正在利用这个function,你并不在乎。 在我看来,完全避免eval。
我已经看到了精心devise的例子来滥用超越我自己创造力的eval函数。 从安全的angular度来看,不惜一切代价避免,而且甚至要求它至less是PHPconfiguration中的一个选项,而不是“给定的”。
除了安全问题之外,eval()不能被编译,优化或者操作码caching,因此总是会比正常的php代码慢得多。 因此,使用eval是非高性能的,所有这一切都不会使它变得邪恶。 ( goto
是邪恶的, eval
只是坏习惯/臭编/难看)
这里是一个解决scheme来运行从数据库中拉出的PHP代码,而不使用eval。 允许范围内的所有function和例外:
$rowId=1; //database row id $code="echo 'hello'; echo '\nThis is a test\n'; echo date(\"Ymd\");"; //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');
基本上,它创build一个独特的function与包含在一个文本文件中的代码,包括该文件,调用该函数,然后删除文件完成后。 我正在使用它来执行每日数据库提取/同步,每一步都需要独特的代码来处理。 这已经解决了我所面临的所有问题。