Когда eval evil в php?

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

Ответы

Ответ 1

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

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

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

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

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

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

  • Иногда eval() является единственным/правильным решением.
  • В большинстве случаев нужно попробовать что-то еще.
  • Если вы не уверены, перейдите 2.
  • Else, быть очень осторожным.

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

В этом случае 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];

Ответ 5

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

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

Ответ 6

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

Это плохое использование, которое мы делаем из этого, что может привести к инъекции кода и быть злым.

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

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

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

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

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

Ответ 7

Я нагло украду содержимое здесь:

  • Эваль по своей природе всегда будет проблемой безопасности.

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

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

Ответ 8

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

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

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

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

1) Это может нанести вред 2) Это может просто сбой

и один, который более-социальный, чем технический:

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

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

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

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

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

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

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

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

Ответ 9

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

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

Ответ 10

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

  • по соображениям безопасности
  • по соображениям производительности
  • для удобства чтения/повторного использования.
  • для целей IDE/инструмента.
  • для объяснения причин
  • всегда есть лучший способ

Ответ 11

eval оценивает строку как код, проблема в том, что если строка каким-либо образом "испорчена", она может вызывать огромные угрозы безопасности. Обычно проблема заключается в том, что пользовательский ввод оценивается в строке, во многих случаях пользователь может вводить код (например, php или ssi), который затем выполняется в пределах eval, он будет работать с теми же правами, что и ваш php script и может использоваться для получения информации/доступа к вашему серверу. Это может быть довольно сложным, чтобы убедиться, что пользовательский ввод правильно очищен, прежде чем передавать его на eval. Существуют и другие проблемы... некоторые из которых являются спорными

Ответ 12

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

Ответ 13

Другая причина eval - зло, что она не может кэшироваться кешами байт-кода PHP, такими как eAccelertor или ACP.

Ответ 14

Это плохое программирование, которое делает eval() злом, а не функцией. Я иногда использую его, так как не могу обойти его в динамическом программировании на нескольких сайтах. Я не могу обрабатывать PHP на одном сайте, потому что я не получу то, что хочу. Я бы просто получил результат! Я счастлив, что функция eval() существует, поскольку это делает мою жизнь намного более легкой. User-вход? Только злоумышленники подключаются хакерами. Я не беспокоюсь об этом.

Ответ 15

Это плохое программирование, которое делает eval() злом, а не функцией. Я иногда использую его, так как не могу обойти его в динамическом программировании на нескольких сайтах. Я не могу обрабатывать PHP на одном сайте, потому что я не получу то, что хочу. Я бы просто получил результат! Я счастлив, что функция eval() существует, поскольку это делает мою жизнь намного более легкой. User-вход? Только злоумышленники подключаются хакерами. Я не беспокоюсь об этом.

Я предскажу, что скоро у вас будут серьезные проблемы...

Во всей честности, нет абсолютно никакой пользы для непомерной функции, такой как eval, в интерпретируемом языке, таком как PHP. Я никогда не видел, чтобы eval выполнял программные функции, которые не могли быть выполнены с использованием других, более безопасных способов...

Eval - это корень всего зла, я полностью согласен, всем людям, которые думают, что тестирование пользовательского ввода поможет. Подумайте дважды, пользовательский ввод может прийти во многих разных формах, и, как мы говорим, хакеры используют эту функцию, о которой вам было неинтересно. На мой взгляд, просто избегайте вообще.

Я видел обработанные примеры злоупотребления функцией eval, которая превзошла мое собственное творчество. Из соображений безопасности избегайте любой ценой, и я бы даже зашел так далеко, что потребовал, чтобы она была, по крайней мере, вариантом в конфигурации PHP, а не "заданной".

Ответ 16

Я часто использовал eval(), но я нашел большинство случаев, когда вам не нужно использовать eval для выполнения трюков. Ну, у вас есть call_user_func() и call_user_func_array() в PHP. Это достаточно хорошо для статического и динамического вызова любого метода.

Чтобы выполнить статический вызов, постройте свой обратный вызов как массив ('class_name', 'method_name') или даже такую ​​простую строку, как 'class_name:: method_name'. Чтобы выполнить обратный вызов стиля использования динамического вызова ($ object, 'method').

Единственное разумное использование для eval() - это написать собственный компилятор. Я сделал один, но eval по-прежнему злой, потому что так чертовски трудно отлаживать. Хуже всего то, что фатальная ошибка в выведенном коду выдает код, вызывающий его. Я использовал расширение Parsekit PECL, чтобы проверить синтаксис по крайней мере, но все равно не радость - попробуйте обратиться к неизвестным классам и сбоям всего приложения.

Ответ 17

Вот решение для запуска 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');

В принципе, он создает уникальную функцию с включенным кодом в текстовый файл, включает в себя файл, вызывает функцию, а затем удаляет файл по завершении работы с ним. Я использую это для ежедневного приема/синхронности базы данных, где для каждого шага требуется уникальный код для обработки. Это решило все проблемы, с которыми я столкнулся.

Ответ 18

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