stringExp = "2^4"
intVal = int(stringExp) # Expected value: 16
Это возвращает следующую ошибку:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int()
with base 10: '2^4'
Я знаю, что это eval
можно обойти, но разве нет лучшего и, что более важно, более безопасного метода для оценки математического выражения, которое хранится в строке?
Ответы:
Pyparsing может использоваться для анализа математических выражений. В частности, fourFn.py показывает, как анализировать основные арифметические выражения. Ниже я преобразовал fourFn в класс числового анализатора для упрощения повторного использования.
Вы можете использовать это так
источник
eval
злоПримечание: даже если вы используете набор
__builtins__
дляNone
него по- прежнему можно было бы выйти с помощью самоанализа:Вычислить арифметическое выражение с помощью
ast
Вы можете легко ограничить допустимый диапазон для каждой операции или любого промежуточного результата, например, чтобы ограничить входные аргументы для
a**b
:Или ограничить величину промежуточных результатов:
пример
источник
import math
?ast.parse
это небезопасно. Напримерast.parse('()' * 1000000, '<string>', 'single')
вылетает интерпретатор.if len(expr) > 10000: raise ValueError
.len(expr)
проверки? Или вы думаете, что в реализации Python есть ошибки, и поэтому невозможно написать безопасный код в целом?Некоторые более безопасные альтернативы
eval()
и * :sympy.sympify().evalf()
* SymPy
sympify
также небезопасен в соответствии со следующим предупреждением из документации.источник
Итак, проблема с eval в том, что он может слишком легко выйти из песочницы, даже если вы избавитесь от
__builtins__
. Все методы выхода из песочницы сводятся к использованиюgetattr
илиobject.__getattribute__
(через.
оператор) для получения ссылки на некоторый опасный объект через разрешенный объект (''.__class__.__bases__[0].__subclasses__
или аналогичный).getattr
устраняется установкой__builtins__
наNone
.object.__getattribute__
- сложный, так как его нельзя просто удалить, потому чтоobject
он неизменяем и потому, что его удаление сломало бы все. Однако__getattribute__
доступ к нему возможен только через.
оператор, поэтому его достаточно, чтобы eval не смог выйти из своей песочницы.При обработке формул единственное допустимое использование десятичной дроби - это когда ей предшествует или следует
[0-9]
, поэтому мы просто удаляем все остальные экземпляры.
.Обратите внимание, что, хотя python обычно обрабатывает
1 + 1.
как1 + 1.0
, это удалит трейлинг.
и оставит вас с1 + 1
. Вы можете добавить)
,и
EOF
в список вещей, которым разрешено следовать.
, но зачем беспокоиться?источник
.
верен ли аргумент об удалении в данный момент, это оставляет возможность для уязвимостей безопасности, если в будущих версиях Python будет введен новый синтаксис, позволяющий получить доступ к небезопасным объектам или функциям каким-либо другим способом. Это решение уже небезопасно в Python 3.6 из F-строки, которые позволяют сделать следующие атаки:f"{eval('()' + chr(46) + '__class__')}"
. Решение, основанное на белых списках, а не на черных, будет более безопасным, но на самом деле лучше решить эту проблемуeval
вообще без них .Вы можете использовать модуль ast и написать NodeVisitor, который проверяет, является ли тип каждого узла частью белого списка.
Поскольку он работает через белый список, а не черный список, это безопасно. Он может получить доступ только к тем функциям и переменным, к которым вы явно предоставили ему доступ. Я заполнил словарь математическими функциями, чтобы вы могли легко предоставить доступ к ним, если хотите, но вы должны использовать его явно.
Если строка пытается вызвать функции, которые не были предоставлены, или вызвать какие-либо методы, будет возбуждено исключение и оно не будет выполнено.
Поскольку здесь используются встроенные в Python синтаксический анализатор и анализатор, он также наследует правила приоритета и продвижения Python.
Приведенный выше код был протестирован только на Python 3.
При желании вы можете добавить к этой функции декоратор тайм-аута.
источник
Причина
eval
иexec
настолько опасны, чтоcompile
функция по умолчанию будет генерировать байт-код для любого допустимого выражения python, а по умолчаниюeval
илиexec
будет выполнять любой действительный байт-код python. Все ответы на сегодняшний день сосредоточены на ограничении байт-кода, который может быть сгенерирован (путем дезинфекции ввода), или создании собственного языка для конкретной предметной области с использованием AST.Вместо этого вы можете легко создать простую
eval
функцию, которая не способна делать что-либо гнусное и может легко выполнять проверки памяти или времени. Конечно, если это простая математика, тогда есть ярлык.Это работает просто: любое математическое выражение константы безопасно вычисляется во время компиляции и сохраняется как константа. Объект кода, возвращаемый компиляцией, состоит из
d
байт-кода, заLOAD_CONST
которым следует номер загружаемой константы (обычно последней в списке), заS
которым следует байт-код дляRETURN_VALUE
. Если этот ярлык не работает, это означает, что вводимые пользователем данные не являются постоянным выражением (содержат переменную или вызов функции или подобное).Это также открывает двери для некоторых более сложных форматов ввода. Например:
Это требует фактической оценки байт-кода, что все еще довольно просто. Байт-код Python - это язык, ориентированный на стек, поэтому все просто
TOS=stack.pop(); op(TOS); stack.put(TOS)
или похоже. Ключ состоит в том, чтобы реализовать только безопасные коды операций (загрузка / сохранение значений, математические операции, возврат значений), а не небезопасные (поиск атрибутов). Если вы хотите, чтобы пользователь мог вызывать функции (вся причина не использовать приведенный выше ярлык), просто сделайте свою реализациюCALL_FUNCTION
только разрешающих функций в «безопасном» списке.Очевидно, настоящая версия этого была бы немного длиннее (существует 119 кодов операций, 24 из которых связаны с математикой). Добавление
STORE_FAST
и пара других позволят вводить подобные'x=5;return x+x
или похожие, тривиально легко. Его даже можно использовать для выполнения функций, созданных пользователем, при условии, что созданные пользователем функции сами выполняются через VMeval (не делайте их вызываемыми !!! или они могут где-то использоваться как обратный вызов). Обработка циклов требует поддержкиgoto
байт-кодов, что означает переход сfor
итератораwhile
на текущую инструкцию и поддержание указателя на нее, но это не слишком сложно. Для обеспечения устойчивости к DOS основной цикл должен проверять, сколько времени прошло с начала вычисления, и некоторые операторы должны отклонять ввод сверх некоторого разумного предела (BINARY_POWER
самый очевидный).Хотя этот подход несколько длиннее, чем простой синтаксический анализатор грамматики для простых выражений (см. Выше о простом захвате скомпилированной константы), он легко распространяется на более сложный ввод и не требует работы с грамматикой (
compile
возьмите что-нибудь произвольно сложное и сведите его к последовательность простых инструкций).источник
Думаю, я бы использовал
eval()
, но сначала проверил бы, является ли строка допустимым математическим выражением, а не чем-то вредоносным. Вы можете использовать регулярное выражение для проверки.eval()
также принимает дополнительные аргументы, которые можно использовать для ограничения пространства имен, в котором он работает, для большей безопасности.источник
+
,-
,*
,/
,**
,(
,)
или что - то более сложнымeval()
если вы не контролируете ввод, даже если вы ограничиваете пространство имен, например,eval("9**9**9**9**9**9**9**9", {'__builtins__': None})
потребляет ЦП, память.Это очень поздний ответ, но я думаю, что он будет полезен для справок в будущем. Вместо того, чтобы писать собственный математический анализатор (хотя приведенный выше пример pyparsing великолепен), вы можете использовать SymPy. У меня нет большого опыта работы с ним, но он содержит гораздо более мощный математический движок, чем кто-либо может написать для конкретного приложения, и базовая оценка выражения очень проста:
Действительно, очень здорово! A
from sympy import *
обеспечивает гораздо больше поддержки функций, таких как триггерные функции, специальные функции и т. Д., Но я избегал этого здесь, чтобы показать, что и откуда.источник
evalf
не принимает numpy ndarrays.sympy.sympify("""[].__class__.__base__.__subclasses__()[158]('ls')""")
эти звонки,subprocess.Popen()
которые я передалls
вместоrm -rf /
. Индекс, вероятно, будет другим на других компьютерах. Это вариант эксплойта Неда[Я знаю, что это старый вопрос, но стоит указывать на новые полезные решения по мере их появления]
Начиная с python3.6, эта возможность теперь встроена в язык , названный «f-strings» .
См .: PEP 498 - Интерполяция буквальной строки
Например (обратите внимание на
f
префикс):источник
str(eval(...))
, поэтому, конечно, не безопаснееeval
.Используйте
eval
в чистом пространстве имен:Чистое пространство имен должно предотвращать внедрение. Например:
В противном случае вы получите:
Возможно, вы захотите предоставить доступ к математическому модулю:
источник
eval("""[i for i in (1).__class__.__bases__[0].__subclasses__() if i.__name__.endswith('BuiltinImporter')][0]().load_module('sys').modules['sys'].modules['os'].system('/bin/sh')""", {'__builtins__': None})
выполняет оболочку Борна ...This is not safe
- ну, я считаю, что это так же безопасно, как и использование bash в целом. Кстати:eval('math.sqrt(2.0)')
<- "математика". требуется, как написано выше.Вот мое решение проблемы без использования eval. Работает с Python2 и Python3. Это не работает с отрицательными числами.
test.py
solution.py
источник