Вопрос-Ответ

Using python's eval() vs. ast.literal_eval()

Использование python eval() против ast.literal_eval()

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

Моя ситуация такова, что у меня есть входные данные, предоставляемые пользователем:

datamap = input('Provide some data here: ')

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

datamap = eval(input('Provide some data here: ')
if not isinstance(datamap, dict):
return

Я прочитал документы, и мне все еще неясно, будет ли это безопасно или нет. Eval оценивает данные сразу после их ввода или после того, как datamap вызывается переменная?

Является ли ast модуль .literal_eval() единственным безопасным вариантом?

Переведено автоматически
Ответ 1

datamap = eval(input('Provide some data here: ')) означает, что вы на самом деле оцениваете код, прежде чем сочтете его небезопасным. Он оценивает код сразу после вызова функции. Смотрите также об опасностях eval.

ast.literal_eval вызывает исключение, если входные данные не являются допустимым типом данных Python, поэтому код не будет выполнен, если это не так.

Используйте ast.literal_eval всякий раз, когда вам нужно eval. Обычно вам не следует оценивать буквальные операторы Python.

Ответ 2

ast.literal_eval() допустимым считается только небольшое подмножество синтаксиса Python:


Предоставленная строка или узел могут состоять только из следующих литеральных структур Python: строк, байтов, чисел, кортежей, списков, dicts, наборов, логических значений и None.


Передача __import__('os').system('rm -rf /a-path-you-really-care-about') в ast.literal_eval() вызовет ошибку, но eval() с радостью удалит ваши файлы.

Поскольку похоже, что вы разрешаете пользователю вводить только простой словарь, используйте ast.literal_eval(). Он безопасно выполняет то, что вы хотите, и ничего больше.

Ответ 3

eval: Это очень мощный метод, но также очень опасный, если вы принимаете строки для вычисления из ненадежных входных данных. Предположим, что вычисляемая строка - "os.system ('rm -rf /')" ? Это действительно приведет к удалению всех файлов на вашем компьютере.


ast.literal_eval: безопасно вычислять узел выражения или строку, содержащую литерал Python или отображение контейнера. Предоставленная строка или узел может состоять только из следующих литеральных структур Python: строк, байтов, чисел, кортежей, списков, dicts, наборов, логических значений, None, байтов и наборов.


Синтаксис:

eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)

Пример:

# python 2.x - doesn't accept operators in string format
import ast
ast.literal_eval('[1, 2, 3]') # output: [1, 2, 3]
ast.literal_eval('1+1') # output: ValueError: malformed string


# python 3.0 -3.6
import ast
ast.literal_eval("1+1") # output : 2
ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'}
# type dictionary
ast.literal_eval("",{}) # output : Syntax Error required only one parameter
ast.literal_eval("__import__('os').system('rm -rf /')") # output : error

eval("__import__('os').system('rm -rf /')")
# output : start deleting all the files on your computer.
# restricting using global and local variables
eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{})
# output : Error due to blocked imports by passing '__builtins__':{} in global

# But still eval is not safe. we can access and break the code as given below
s = """
(lambda fc=(
lambda n: [
c for c in
().__class__.__bases__[0].__subclasses__()
if c.__name__ == n
][0]
):
fc("function")(
fc("code")(
0,0,0,0,"KABOOM",(),(),(),"","",0,""
),{}
)()
)()
"""

eval(s, {'__builtins__':{}})

В приведенном выше коде ().__class__.__bases__[0] ничего, кроме самого объекта.
Теперь мы создали экземпляры всех подклассов, здесь наша основная enter code hereцель - найти из него один класс с именем n.

Нам нужно code object и function object из созданных экземпляров подклассов. Это альтернативный способ из CPython получить доступ к подклассам object и подключить систему.

В python 3.7 ast.literal_eval() теперь более строгий. Сложение и вычитание произвольных чисел больше не разрешено. Ссылка

Ответ 4

Python нетерпелив в своей оценке, поэтому eval(input(...)) (Python 3) оценит ввод пользователя, как только он попадет в eval, независимо от того, что вы делаете с данными впоследствии. Следовательно, это небезопасно, особенно при eval пользовательском вводе.

Использование ast.literal_eval.


В качестве примера, ввод этого параметра в командной строке может быть очень плохим для вас:

__import__('os').system('rm -rf /a-path-you-really-care-about')
2023-09-29 12:57 python