Почему использовать eval() опасно?

Я пишу код для калькулятора (т.е., вписал пример и получил ответ).

Я нашёл способ использовать eval(), но говорят, что он опасен.

Чем он опасен и чем можно заменить его?


Ответы (2 шт):

Автор решения: Justiks

Документация python:

eval(expression, globals=None, locals=None)

Параметры:
expression (str | code object) – Python выражение (Проще Python код)
...

Аргумент expression анализируется и оценивается как Python выражение (Проще Python код) ...

То есть, eval буквально позволяет выполнить все, что вы в него сунете. Представьте, как пользователь вашего калькулятора пишет в поле ввода os.shutdown(). Ваш код:

expression = input()
eval(exspression)

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

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

При желании eval можно заменить простой функцией, к примеру:

def main():
    expression = input()

    first_number = ""
    index = 0

    while expression[index].isdigit():
        first_number += expression[index]
        index += 1

    operator = expression[index]
    second_number = expression[index+1:]

    if operator == "+":
        print(int(first_number) + int(second_number))
    elif operator == "-":
        print(int(first_number) - int(second_number))
    elif operator == "*":
        print(int(first_number) * int(second_number))
    elif operator == "/":
        print(int(first_number) / int(second_number))
→ Ссылка
Автор решения: Amgarak

В контексте калькулятора, накидал простую проверку на безопасность входного выражения:

import math
import re

def safe_eval(string):
    allowed_chars = set('0123456789+-*/(). aceinopqstr')
    danger_keys = {'not', 'is', 'in', 'or', 'and', 'assert', 'raise', 'import', 'exec', 'eval', 'Ellipsis', 'as', 'case', 'pass'}

    if set(string) - allowed_chars:
        raise ValueError("Выражение содержит недопустимые символы")
    
    for keyword in danger_keys:
        if re.search(r'\b' + re.escape(keyword) + r'\b', string):
            raise ValueError("Выражение содержит недопустимые ключевые слова")

    return eval(string, {'__builtins__': {'sin': math.sin, 'cos': math.cos, 'tan': math.tan, 'sqrt': math.sqrt, 'pi': math.pi, 'e': math.e}})

safe = "sqrt(tan(54) - cos((2.8 + 3) * sin(pi / 4) + 10.5e-3))"
try:
    result = safe_eval(safe)
    print(f"Результат: {result}")
except Exception as e:
    print(f"Ошибка: {e}")

danger = "__import__('os').shutdown()"
try:
    result = safe_eval(danger)
    print(f"Результат: {type(result)}")
except Exception as e:
    print(f"Ошибка: {e}")
→ Ссылка