php 中的 eval 何时是邪恶的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/951373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 00:25:50  来源:igfitidea点击:

When is eval evil in php?

phpeval

提问by Pierre Spring

In all the years I have been developing in php, I've always heard that using eval()is evil.

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

Considering the following code, wouldn't it make sense to use the second (and more elegant) option? If not, why?

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

// $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).';');

回答by Micha? Rudnicki

I would be cautious in calling eval() pure evil.Dynamic evaluation is a powerful tool and can sometimes be a life saver. With eval() one can work around shortcomings of PHP (see below).

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

The main problems with eval() are:

eval() 的主要问题是:

  • Potential unsafe input.Passing an untrusted parameter is a way to fail. It is often not a trivial task to make sure that a parameter (or part of it) is fully trusted.
  • Trickiness.Using eval() makes code clever, therefore more difficult to follow. To quote Brian Kernighan "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it"
  • 潜在的不安全输入。传递不受信任的参数是一种失败的方式。确保参数(或其中的一部分)是完全可信的,这通常不是一项微不足道的任务。
  • 棘手。使用 eval() 使代码更聪明,因此更难理解。引用 Brian Kernighan 的话说:“调试的难度是编写代码的两倍。因此,如果您尽可能聪明地编写代码,根据定义,您就不够聪明来调试它

The main problem with actual use of eval() is only one:

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

  • Inexperienced developers who use it without enough consideration.
  • 没有充分考虑就使用它的缺乏经验的开发人员。

As a rule of thumb I tend to follow this:

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

  1. Sometimes eval() is the only/the right solution.
  2. For most cases one should try something else.
  3. If unsure, goto 2.
  4. Else, be very, very careful.
  1. 有时 eval() 是唯一/正确的解决方案。
  2. 在大多数情况下,应该尝试其他方法。
  3. 如果不确定,请转到 2。
  4. 否则,要非常非常小心。

回答by Patrick Cornelissen

eval is evil when there is only the slightest possibility that userinput is included in the evaluated string. When you do eval without content that came from a user, you should be safe.

当用户输入包含在评估字符串中的可能性很小时, eval 是邪恶的。当您在没有来自用户的内容的情况下进行评估时,您应该是安全的。

Nevertheless you should think at least twice before using eval, it looks deceivingly simple, but with error handling (see VBAssassins comment), debuggability etc. in mind, it is not so simple anymore.

尽管如此,在使用 eval 之前你应该至少三思,它看起来非常简单,但考虑到错误处理(见 VBAssassins 评论)、可调试性等,它不再那么简单了。

So as a rule of thumb: Forget about it. When eval is the answer you're propably asking the wrong question! ;-)

所以作为一个经验法则:忘记它。当 eval 是答案时,您可能问错了问题!;-)

回答by thomasrutter

eval() is equally evil at all times.

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

"When is eval() not evil?" is the wrong question to be asking in my opinion, because it seems to imply that the drawbacks to using eval() magically disappear in some contexts.

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

Using eval() is generally a bad idea because it decreases readability of code, the ability for you to predict the code path (and possible security implications of that) before runtime, and hence the ability to debug code. Using eval() can also prevent the evaluated code and the code surrounding it from being optimised by an opcode cache such as the Zend Opcache integrated into PHP 5.5 and above, or by a JIT compiler such as the one in HHVM.

使用 eval() 通常是一个坏主意,因为它会降低代码的可读性、在运行之前预测代码路径(以及可能的安全隐患)的能力,以及调试代码的能力。使用 eval() 还可以防止被评估的代码及其周围的代码被操作码缓存优化,例如集成到 PHP 5.5 及更高版本中的 Zend Opcache,或 JIT 编译器(例如 HHVM 中的编译器)。

Furthermore, there is no situation for which it is absolutely necessary to use eval() - PHP is a fully-capable programming language without it.

此外,没有绝对必要使用 eval() 的情况——没有它,PHP 是一种功能齐全的编程语言。

Whether or not you actually see these as evils or you can personally justify using eval() in some cases is up to you. To some, the evils are too great to ever justify it, and to others, eval() is a handy shortcut.

您是否真的将这些视为邪恶,或者您是否可以在某些情况下亲自证明使用 eval() 是由您决定的。对某些人来说,罪恶太大而无法证明它是合理的,而对另一些人来说,eval() 是一个方便的捷径。

However, if you see eval() as evil, it's evil at all times. It does not magically lose its evilness depending on context.

然而,如果你认为 eval() 是邪恶的,那么它在任何时候都是邪恶的。它不会根据上下文神奇地失去它的邪恶。

回答by BlackAura

In this case, eval is probably safe enough, as long as it's never possible for arbitrary columns to be created in a table by a user.

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

It's not really any more elegant though. This is basically a text parsing problem, and abusing PHP's parser to handle is seems a bit hacky. If you want to abuse language features, why not abuse the JSON parser? At least with the JSON parser, there's no possibility at all of code injection.

虽然它真的不是更优雅。这基本上是一个文本解析问题,滥用 PHP 的解析器来处理似乎有点 hacky。如果你想滥用语言特性,为什么不滥用 JSON 解析器呢?至少对于 JSON 解析器,根本不可能进行代码注入。

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

A regular expression is probably the most obvious way. You can use a single regular expression to extract all the values from this 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];

回答by Alix Axel

eval()is slow, but I wouldn't call it evil.

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

It's the bad use we make of it that can lead to code injectionand be evil.

我们对它的不良使用会导致代码注入并变得邪恶。

A simple example:

一个简单的例子:

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

A harmlful example:

一个有害的例子:

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

I would advise you to not use eval()but if you do, make sure you validate / whitelist all input.

我建议您不要使用,eval()但如果您使用,请确保您验证/将所有输入列入白名单。

回答by GreenieMeanie

When you are using foreign data (such as user input) inside the eval.

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

In your example above, this isn't an issue.

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

回答by stefs

i'll blatantly steal the content here:

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

  1. Eval by its nature is always going to be a security concern.

  2. Besides security concerns eval also has the problem of being incredibly slow. In my testing on PHP 4.3.10 its 10 times slower then normal code and 28 times slower on PHP 5.1 beta1.

  1. Eval 本质上总是会引起安全问题。

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

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

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

回答by Francois Bourgeois

eval()is alwaysevil.

eval()总是邪恶的。

  • for security reasons
  • for performance reasons
  • for readability / reusability reasons
  • for IDE / tool reasons
  • for debugging reasons
  • there is always a better way
  • 出于安全原因
  • 出于性能原因
  • 出于可读性/可重用性的原因
  • 出于 IDE/工具的原因
  • 出于调试原因
  • 总有更好的方法

回答by Francois Bourgeois

I'd also pay some consideration to people maintaining your code.

我也会考虑维护你的代码的人。

eval() isn't the easiet to just look at and know what is supposed to happen, your example isn't so bad, but in other places it can be a right nightmare.

eval() 不是简单地查看并知道应该发生什么,您的示例还不错,但在其他地方,它可能是一场正确的噩梦。

回答by Parsingphase

Personally, I think that code's still pretty evil because you're not commenting what it's doing. It's also not testing its inputs for validity, making it very fragile.

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

I also feel that, since 95% (or more) of uses of eval are actively dangerous, the small potential time saving that it might provide in other cases isn't worth indulging in the bad practice of using it. Plus, you'll later have to explain to your minions why your use of eval is good, and theirs bad.

我还认为,由于 eval 的 95%(或更多)使用是积极危险的,因此它在其他情况下可能提供的少量潜在时间节省不值得沉迷于使用它的不良做法。另外,您稍后必须向您的下属解释为什么您使用 eval 是好的,而他们的则是坏的。

And, of course, your PHP ends up looking like Perl ;)

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

There are two key problems with eval(), (as an "injection attack" scenario):

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

1) It may cause harm 2) It may simply crash

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

and one that's more-social-than-technical:

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

3) It'll tempt people to use it inappropriately as a shortcut elsewhere

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

In the first case, you run the risk (obviously, not when you're eval'ing a known string) of arbitrary code execution. Your inputs may not be as known or as fixed as you think, though.

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

More likely (in this case) you'll just crash, and your string will terminate with a gratuitously obscure error message. IMHO, all code should fail as neatly as possible, failing which it should throw an exception (as the most handleable form of error).

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

I'd suggest that, in this example, you're coding by coincidence rather than coding to behaviour. Yes, the SQL enum statement (and are you sure that field's enum? - did you call the right field of the right table of the right version of the database? Did it actually answer?) happens to look like array declaration syntax in PHP, but I'd suggest what you really want to do is not find the shortest path from input to output, but rather tackle the specified task:

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

  • Identify that you have an enum
  • Extract the inner list
  • Unpack the list values
  • 确定您有一个枚举
  • 提取内部列表
  • 解压列表值

Which is roughly what your option one does, but I'd wrap some if's and comments around it for clarity and safety (eg, if the first match doesn't match, throw exception or set null result).

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

There are still some possible issues with escaped commas or quotes, and you should probably unpack the data then de-quote it, but it does at least treat data as data, rather than as code.

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

With the preg_version your worst outcome is likely to be $result=null, with the eval version the worst is unknown, but at least a crash.

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