Comments 25
Вот хоть убей не знаю, зачем вообще кому-то может потребоваться eval, тем более — в Питоне. Только по дурости и/или лености.
Во славу сатаны, конечно же. Даже название намекает.
Насколько я успел заметить, eval часто применяется в задачах из серии кодогенерации. Тот же cog в конечном счёте eval-ит код: bitbucket.org/ned/cog/src/2fbe1f31bd50ba9ef76e3d9fb2d18165a3daeca5/cogapp/cogapp.py
P.S. я сейчас отвечаю на вопрос, а не оправдываю его применение, если что.)
P.S. я сейчас отвечаю на вопрос, а не оправдываю его применение, если что.)
да и не только в питоне, но и в PHP
Почти во всех скриптовых языках есть eval. Это миллионная статья про то почему eval опасен. Но он не так страшен как его малюют и у него есть хорошее применение.
В eval нельзя никогда исполнять то что получено из внешнего источника.
Eval хорош в единственной вещи: метапрограммирование + оптимизация. Самый быстрый mysql драйвер под node.js написан с применением eval. Eval позволяет скомпилировать один раз функцию под конкретную окружающую среду. В случае драйвера mysql генерируется функция для прямого мэппинга отдачи объектов.
В случае написания высоконагруженных приложений рендереры могут стать узким местом, потому почти все библиотеки форматирования даты, заточенные на скорость используют eval. coolDateFormater('Y-m-d') -> вернёт функцию, которая примет на вход дату, а внутри не будет разбирать формат, а сразу возьмёт из даты год, месяц, день и отдаст: Год +'-'+ месяц +'-'+ день. Рендереры не всегда про визуальную часть, в моделях зачастую данные перегоняются в свой формат и на 1к таких операций это уже может дать эффект, а на 10к стать заметным невооруженным глазом.
В eval нельзя никогда исполнять то что получено из внешнего источника.
Eval хорош в единственной вещи: метапрограммирование + оптимизация. Самый быстрый mysql драйвер под node.js написан с применением eval. Eval позволяет скомпилировать один раз функцию под конкретную окружающую среду. В случае драйвера mysql генерируется функция для прямого мэппинга отдачи объектов.
function(data){
return {id: data[0], name: data[1], date: new Date(data[2])};
}
В случае написания высоконагруженных приложений рендереры могут стать узким местом, потому почти все библиотеки форматирования даты, заточенные на скорость используют eval. coolDateFormater('Y-m-d') -> вернёт функцию, которая примет на вход дату, а внутри не будет разбирать формат, а сразу возьмёт из даты год, месяц, день и отдаст: Год +'-'+ месяц +'-'+ день. Рендереры не всегда про визуальную часть, в моделях зачастую данные перегоняются в свой формат и на 1к таких операций это уже может дать эффект, а на 10к стать заметным невооруженным глазом.
Лично у меня есть паттерн observable оптимизированный через eval. Вместо обхода списка всех подписчиков — на каждое подписывание перегенерируется функция, которая вызывает всех подписчиков в нужном скоупе с переданными параметрами. Дало ускорение ~30%. На богатых на эвенты моделях такой подход сказался положительно. (Хотя кого я обманываю, я люблю оптимизировать и эта задача была сделана just for fun. Так как скорость решения оказалась выше обычной реализации — стал использовать эту).
Например, в стандартной библиотеке collections.named_tuple реализован именно через eval (формалньно — через exec, но невелика разница). hg.python.org/cpython/file/ab5e2b0fba15/Lib/collections/__init__.py#l239
Наверное, более чистым способом было бы аккуратно собрать AST-дерево и скомпилировать его, благо возможности для того есть. Но это сложнее, и, возможно, даже медленнее.
Наверное, более чистым способом было бы аккуратно собрать AST-дерево и скомпилировать его, благо возможности для того есть. Но это сложнее, и, возможно, даже медленнее.
На вскидку, про динамическое подключение модулей
eval — очень удобный способ фильтрации чего угодно по сложным пользовательским выражениям (найти все ноутбуки в продаже, от 10 000 до 20 000, но не acer, либо можно acer, но если до 12 000 и выпущен в этом году и памяти больше 8G, либо любой ноут с USB 3.0 если меньше 10 000, и при этом не учитывать предложения фирм, которые в моем черном списке).
Обычный интерфейс «с галочками» как на яндекс-маркете, во-первых очень сложно реализуется (особенно, если нам его надо для многих видов товара сделать), во-вторых — гораздо менее гибкий (попробуйте-ка вышеописанное выражение выразить в виде «галочек» на форме фильтра в маркете)
ниже дал ссылку на свой пост с обсуждением этой проблемы и безопасным решением
Обычный интерфейс «с галочками» как на яндекс-маркете, во-первых очень сложно реализуется (особенно, если нам его надо для многих видов товара сделать), во-вторых — гораздо менее гибкий (попробуйте-ка вышеописанное выражение выразить в виде «галочек» на форме фильтра в маркете)
ниже дал ссылку на свой пост с обсуждением этой проблемы и безопасным решением
Есть такое:
code.activestate.com/recipes/496746-restricted-safe-eval/
Проблемы с os. запросами решается через setuid setgid seteuid setegid chroot
Но конечно лучше eval не использовать в оригинале.
code.activestate.com/recipes/496746-restricted-safe-eval/
Проблемы с os. запросами решается через setuid setgid seteuid setegid chroot
Но конечно лучше eval не использовать в оригинале.
В TCL есть замечательная штука — safe interp. Которая как раз решает подобные проблемы. Почему другие языки не возьмут на вооружение эту штуку?
Пробовали, не получается. Есть у меня подозрение, что с TCL оно работает только в силу малараспространённости TCL'я. Причём NSA, скорее всего, умеет вскрывать сервера, использующие safe interp, а остальным оно просто не нужно.
Вы неправы. В TCL оно сделано строго по принципу «запрещено всё, что не разрешено», т.е. по белым спискам (в отличие от того, что например описано в статье выше — там чёрные списки). То есть, взломать конечно можно, если разрешили неправильно (скажем, сделали алиас небезопасной функции в интерп, а проверку при вызове алиаса выполнили недостаточно тщательно), но это не проблема языка или интерпретатора, а явно ошибка в программе, некорректное использование или некорректный (слишком обширный) белый список.
Сообщений же на тему «escape safe interp» в случае с корректным белым списком не обнаруживается, несмотря на то, что в силу структуры самого языка возможностей «сбежать» там должно быть хоть отбавляй.
Сообщений же на тему «escape safe interp» в случае с корректным белым списком не обнаруживается, несмотря на то, что в силу структуры самого языка возможностей «сбежать» там должно быть хоть отбавляй.
docs.python.org/dev/library/ast.html#ast.literal_eval безопасный. Правда, именно поэтому умеет совсем немного.
Интересный материал: us.pycon.org/2014/schedule/presentation/208/ — выступление на пайкон 2014 о создании питонячей песочницы.
слайды
слайды
Некропостну со ссылкой на свой пост по этой теме — evalidate: безопасная обработка пользовательских выражений. Тоже очень долго мучался с безопасностью eval'а (при том что от всей его мощности мне нужно всего-то полпроцента), кончилось написанием своего модуля для обработки только безопасных пользовательских выражений.
Как реализовать REPL без eval?
Sign up to leave a comment.
И снова про опасность eval()