За все годы, которые я развивал в 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 () чистого зла. Динамическая оценка является мощным инструментом и иногда может быть спасателем жизни. С помощью eval () можно обойти недостатки PHP (см. Ниже).
Основные проблемы с eval ():
Основная проблема с фактическим использованием eval () – только одна:
Как правило, я следую этому:
eval – это зло, когда есть только малейшая вероятность включения userinput в оцениваемую строку. Когда вы делаете eval без контента, который поступает от пользователя, вы должны быть в безопасности.
Тем не менее вам следует подумать хотя бы дважды, прежде чем использовать eval, он выглядит обманчиво простым, но с учетом ошибок (см. Комментарий VBAssassins), отладки и т. Д., Это не так просто.
Итак, как правило: Забудьте об этом. Когда eval – это ответ, вы, возможно, задаете неправильный вопрос! 😉
eval () одинаково вреден во все времена.
«Когда eval () не злой?» на мой взгляд, это неправильный вопрос, поскольку он, по-видимому, подразумевает, что недостатки использования eval () волшебным образом исчезают в некоторых контекстах.
Использование eval (), как правило, является плохой идеей, поскольку она снижает читаемость кода, способность предсказать путь кода (и возможные последствия для безопасности) этого процесса до выполнения, а значит, и возможность отладки кода. Использование eval () также может помешать оцениваемому коду и окружающему его коду оптимизировать кеш-код операции, такой как Zend Opcache, интегрированный в PHP 5.5 и выше, или компилятором JIT, таким как JHKM.
Кроме того, нет никакой ситуации, для которой абсолютно необходимо использовать eval () – PHP – это полностью функциональный язык программирования без него.
Независимо от того, действительно ли вы видите их как зло или вы можете лично обосновать использование eval (), в некоторых случаях зависит от вас. Для некоторых, зло слишком велико, чтобы оправдать его, а другим, eval () – удобный ярлык.
Однако, если вы видите eval () как зло, это зло всегда. Он не волшебным образом теряет свое зло в зависимости от контекста.
В этом случае 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];
Когда вы используете внешние данные (например, пользовательский ввод) внутри eval.
В приведенном выше примере это не проблема.
eval()
медленный, но я бы не назвал его злым.
Это плохое использование, которое мы делаем из этого, что может привести к инъекции кода и быть злым.
Простой пример:
$_GET = 'echo 5 + 5 * 2;'; eval($_GET); // 15
Несчастный пример:
$_GET = 'system("reboot");'; eval($_GET); // oops
Я бы посоветовал вам не использовать eval()
но если вы это сделаете, убедитесь, что вы подтвердили / whitelist все входные данные.
я откровенно украду содержимое здесь:
Эваль по своей природе всегда будет проблемой безопасности.
Помимо проблем безопасности, eval также имеет проблемы с невероятно медленными темпами. В моем тестировании на PHP 4.3.10 его в 10 раз медленнее, чем обычный код и 28 раз медленнее на PHP 5.1 beta1.
blog.joshuaeichorn.com: использование-eval-in-php
Лично я считаю, что код по-прежнему довольно злой, потому что вы не комментируете, что он делает. Он также не тестирует свои входы для достоверности, что делает его очень хрупким.
Я также чувствую, что, поскольку 95% (или более) использования eval активно опасны, малое потенциальное экономия времени, которое оно может обеспечить в других случаях, не стоит предаваться плохой практике его использования. Кроме того, позже вам придется объяснить своим миньонам, почему ваше использование eval является хорошим, а их плохо.
И, конечно, ваш PHP заканчивается похожим на Perl;)
Существуют две ключевые проблемы с eval (), (как сценарий «инъекции»):
1) Это может причинить вред 2) Это может просто сбой
и тот, который больше – социальный, чем технический:
3) Это соблазнит людей использовать его ненадлежащим образом в качестве ярлыка в другом месте
В первом случае вы рискуете (очевидно, не тогда, когда вы оцениваете известную строку) произвольного исполнения кода. Однако ваши входы могут быть не такими известными или фиксированными, как вы думаете.
Скорее всего (в этом случае) вы просто потерпите крах, и ваша строка завершится сообщением об ошибке. IMHO, весь код должен сбой настолько аккуратно, насколько это возможно, в противном случае он должен выбросить исключение (как наиболее управляемую форму ошибки).
Я бы предположил, что в этом примере вы кодируете по совпадению, а не по кодированию поведения. Да, оператор enum SQL (и вы уверены, что перечисление поля?), Вы вызвали правильное поле правой таблицы правильной версии базы данных? На самом деле это ответ?), Похоже, выглядит как синтаксис объявления массива в PHP, но я бы предложил то, что вы действительно хотите сделать, это не найти кратчайший путь от ввода к выходу, а скорее решить заданную задачу:
Это примерно то, что делает ваш вариант, но я бы обернул некоторые элементы if и комментарии вокруг этого для ясности и безопасности (например, если первое совпадение не соответствует, выбрасывает исключение или задает нулевой результат).
Есть еще некоторые возможные проблемы с экранированными запятыми или кавычками, и вам, вероятно, следует распаковать данные, а затем де-котируйте их, но он, по крайней мере, рассматривает данные как данные, а не как код.
С preg_version ваш худший результат, скорее всего, будет $ result = null, а версия eval хуже всего неизвестна, но, по крайней мере, сбой.
Я также должен обратить внимание на людей, поддерживающих ваш код.
eval () не является easite, чтобы просто смотреть и знать, что должно случиться, ваш пример не так уж плох, но в других местах это может быть правильный кошмар.
eval оценивает строку как код, проблема в том, что если строка каким-либо образом «испорчена», она может вызывать огромные угрозы безопасности. Обычно проблема заключается в том, что пользовательский ввод оценивается в строке, во многих случаях пользователь может вводить код (например, php или ssi), который затем запускается в пределах eval, он будет работать с теми же разрешениями, что и ваш php-скрипт, и может для получения информации / доступа к вашему серверу. Это может быть довольно сложным, чтобы убедиться, что пользовательский ввод правильно очищен, прежде чем передавать его на eval. Есть и другие проблемы … некоторые из которых являются спорными
PHP советует вам написать свой код таким образом, чтобы он выполнялся с помощью call_user_func, вместо того чтобы делать явные оценки.
eval()
всегда злой.
Другая причина, по которой eval
– зло, заключается в том, что он не может кэшироваться кэшами байт-кода PHP, такими как eAccelertor или ACP.
Это плохое программирование, которое делает eval () злом, а не функцией. Я иногда использую его, так как не могу обойти его в динамическом программировании на нескольких сайтах. Я не могу обрабатывать PHP на одном сайте, потому что я не получу то, что хочу. Я бы просто получил результат! Я счастлив, что функция eval () существует, поскольку это делает мою жизнь намного более легкой. User-вход? Только злоумышленники подключаются хакерами. Я не беспокоюсь об этом.
Я использовал eval () много, но я нашел большинство случаев, когда вам не нужно использовать eval для выполнения трюков. Ну, у вас есть call_user_func () и call_user_func_array () в PHP. Это достаточно хорошо для статического и динамического вызова любого метода.
Для выполнения статического вызова создайте свой обратный вызов как массив ('class_name', 'method_name') или даже такую простую строку, как 'class_name :: method_name'. Чтобы выполнить обратный вызов стиля вызова динамического вызова ($ object, 'method').
Единственное разумное использование для eval () – это написать собственный компилятор. Я сделал один, но eval все еще злой, потому что его так чертовски трудно отлаживать. Хуже всего то, что фатальная ошибка в выведенном коду выдает код, вызывающий его. Я использовал расширение Parsekit PECL для проверки синтаксиса по крайней мере, но до сих пор нет радости – попробуйте обратиться к неизвестным классам и сбоям всего приложения.
Это плохое программирование, которое делает eval () злом, а не функцией. Я иногда использую его, так как не могу обойти его в динамическом программировании на нескольких сайтах. Я не могу обрабатывать PHP на одном сайте, потому что я не получу то, что хочу. Я бы просто получил результат! Я счастлив, что функция eval () существует, поскольку это делает мою жизнь намного более легкой. User-вход? Только злоумышленники подключаются хакерами. Я не беспокоюсь об этом.
Я предсказываю, что скоро у вас будут серьезные проблемы …
Честно говоря, нет абсолютно никакой пользы для непомерной функции, такой как eval, в интерпретируемом языке, таком как PHP. Я никогда не видел, чтобы eval выполнял программные функции, которые не могли быть выполнены с использованием других, более безопасных способов …
Eval является корнем всего зла, я полностью согласен, всем людям, которые думают, что тестирование пользовательского ввода поможет. Подумайте дважды, пользовательский ввод может прийти во многих разных формах, и, как мы говорим, хакеры используют эту функцию, о которой вам было неинтересно. На мой взгляд, просто избегайте вообще.
Я видел обработанные примеры злоупотребления функцией eval, которая превзошла мое собственное творчество. Из соображений безопасности избегайте любой ценой, и я бы даже зашел так далеко, чтобы потребовать, чтобы он был, по крайней мере, вариантом в конфигурации PHP, а не «данным».
Помимо проблем безопасности, eval () не может быть скомпилирован, оптимизирован или кэширован с помощью кода операции, поэтому он будет медленнее – медленнее – чем обычный PHP-код. Таким образом, он не может использовать eval, хотя это и не делает его злым. ( goto
is evil, eval
– только плохая практика / вонючий код / уродливый)
Вот решение для запуска PHP-кода, извлеченного из базы данных без использования eval. Позволяет использовать все функции и исключения в области видимости:
$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');
В основном он создает уникальную функцию с включенным кодом в текстовый файл, включает в себя файл, вызывает функцию, а затем удаляет файл, когда он выполняется. Я использую это для ежедневного приема / синхронности базы данных, где для каждого шага требуется уникальный код для обработки. Это решило все проблемы, с которыми я столкнулся.