Когда Eval зло в PHP? - PullRequest
       111

Когда Eval зло в PHP?

82 голосов
/ 04 июня 2009

За все годы, что я разрабатывал в 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).';');

Ответы [ 18 ]

126 голосов
/ 04 июня 2009

Я бы с осторожностью назвал eval () чистым злом. Динамическая оценка - это мощный инструмент, который иногда может спасти жизнь. С помощью eval () можно обойти недостатки PHP (см. Ниже).

Основные проблемы с eval ():

  • Потенциально небезопасный ввод. Передача ненадежного параметра является способом сбоя. Часто нетрудно убедиться, что параметр (или его часть) полностью доверен.
  • Хитрость. Использование eval () делает код умным, поэтому его сложнее выполнять. Цитируя Брайана Кернигана: « Отладка в два раза сложнее, чем писать код в первую очередь. Поэтому, если вы пишете код настолько умно, насколько это возможно, вы, по определению, недостаточно умны, чтобы отлаживать его »

Основная проблема с фактическим использованием eval () только одна:

  • Неопытные разработчики, которые используют его без достаточного внимания.

Как правило, я склонен следовать этому:

  1. Иногда eval () является единственным / правильным решением.
  2. В большинстве случаев нужно попробовать что-то другое.
  3. Если вы не уверены, перейдите к 2.
  4. Иначе, будь очень, очень осторожен.
38 голосов
/ 04 июня 2009

eval - это зло, когда существует только малейшая вероятность того, что пользовательский вход включен в вычисляемую строку. Когда вы делаете eval без контента, полученного от пользователя, вы должны быть в безопасности.

Тем не менее, вам следует подумать, по крайней мере, дважды, прежде чем использовать eval, это выглядит обманчиво простым, но с учетом обработки ошибок (см. Комментарий VBAssassins), отладки и т. Д., Это уже не так просто.

Итак, как правило: Забудь об этом. Когда eval является ответом, вы, вероятно, задаете неправильный вопрос! ; -)

17 голосов
/ 05 июня 2009

eval () всегда одинаково злой.

"Когда eval () не является злом?" это неправильный вопрос, на мой взгляд, потому что, похоже, подразумевает, что недостатки использования eval () магическим образом исчезают в некоторых контекстах.

Использование eval (), как правило, является плохой идеей, поскольку снижает читабельность кода, возможность предсказывать путь к коду (и возможные последствия для безопасности) перед выполнением и, следовательно, возможность отладки кода. Использование eval () также может предотвратить оптимизацию оцененного кода и кода, его окружающего, с помощью кеша кода операции, такого как Zend Opcache, интегрированного в PHP 5.5 и выше, или JIT-компилятором, например, в HHVM.

Кроме того, не существует ситуации, в которой абсолютно необходимо использовать eval () - без него PHP является полностью совместимым языком программирования.

Независимо от того, видите ли вы на самом деле это как зло, или вы можете лично оправдать использование eval () в некоторых случаях, зависит от вас. Для одних зло слишком велико, чтобы оправдать его, а для других eval () - это удобный способ.

Однако, если вы видите eval () как зло, оно всегда зло. Он волшебным образом не теряет своего зла в зависимости от контекста.

14 голосов
/ 04 июня 2009

В этом случае 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];
11 голосов
/ 04 июня 2009

Когда вы используете сторонние данные (например, пользовательский ввод) внутри eval.

В приведенном выше примере это не проблема.

10 голосов
/ 14 января 2010

eval() медленно, но я бы не назвал это злом.

Это плохое использование, которое мы можем использовать, может привести к внедрению кода и быть злым.

Простой пример:

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

Вредный пример:

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

Я бы посоветовал вам не использовать eval(), но если вы это сделаете, убедитесь, что вы проверяете / вводите в белый список все введенные данные.

7 голосов
/ 04 июня 2009

Я явно украду контент здесь:

  1. Eval по своей природе всегда будет проблемой безопасности.

  2. Помимо проблем безопасности, у eval также есть проблема невероятно медленной работы. В моем тестировании на PHP 4.3.10 он был в 10 раз медленнее, чем обычный код, и в 28 раз медленнее на PHP 5.1 beta1.

blog.joshuaeichorn.com: использование-eval-in-php

5 голосов
/ 04 июня 2009

Лично я думаю, что код все еще довольно злой, потому что вы не комментируете, что он делает. Он также не проверяет свои входные данные на достоверность, что делает его очень хрупким.

Я также чувствую, что, поскольку 95% (или более) видов использования eval активно опасны, небольшая потенциальная экономия времени, которую он может обеспечить в других случаях, не стоит потворствовать плохой практике его использования. Кроме того, вам позже придется объяснить своим миньонам, почему вы используете eval хорошо, а они - плохо.

И, конечно, ваш PHP выглядит как Perl;)

Есть две ключевые проблемы с eval (), (как сценарий "инъекционной атаки"):

1) Может причинить вред 2) Может просто сбой

и более социально-технический:

3) Люди будут соблазнительно использовать его в качестве ярлыка в другом месте

В первом случае вы рискуете (очевидно, не когда вы проверяете известную строку) выполнения произвольного кода. Однако ваши входные данные могут быть не такими известными или фиксированными, как вы думаете.

Скорее всего (в этом случае) вы просто аварийно завершите работу, и ваша строка прекратится с совершенно неясным сообщением об ошибке. ИМХО, весь код должен давать сбой настолько аккуратно, насколько это возможно, в противном случае он должен генерировать исключение (как наиболее поддающаяся обработке форма ошибки).

Я бы предположил, что в этом примере вы кодируете по совпадению, а не по поведению. Да, оператор перечисления SQL (и вы уверены, что перечисление этого поля? - вы называли правильное поле правой таблицы нужной версии базы данных? Он действительно отвечал?) Выглядит как синтаксис объявления массива в PHP, но я бы посоветовал вам не искать кратчайший путь от ввода к выводу, а решить указанную задачу:

  • Укажите, что у вас есть перечисление
  • Извлечь внутренний список
  • Распаковать список значений

Это примерно то, что делает ваш вариант, но я бы обернул некоторые if и прокомментировал его для ясности и безопасности (например, если первое совпадение не совпадает, выдает исключение или устанавливает нулевой результат).

Есть еще некоторые возможные проблемы с экранированными запятыми или кавычками, и вам, вероятно, следует распаковать данные, а затем удалить их из кавычек, но они по крайней мере обрабатывают данные как данные, а не как код.

С preg_version ваш худший результат, вероятно, будет $ result = null, а с версией eval худший неизвестен, но, по крайней мере, вылетает.

4 голосов
/ 31 января 2013

eval() есть всегда зло.

  • по соображениям безопасности
  • по соображениям производительности
  • для удобства чтения / повторного использования
  • по причинам IDE / инструмента
  • по причинам отладки
  • всегда есть лучший способ
4 голосов
/ 04 июня 2009

Я бы также уделил внимание людям, которые поддерживают ваш код.

eval () - это не просто смотреть и знать, что должно произойти, ваш пример не так уж плох, но в других местах это может стать настоящим кошмаром.

...