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

176

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

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

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

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

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

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

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

tijko
источник

Ответы:

190

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

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

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

летучесть
источник
20
Это не на 100% правильный совет, так как любые побитовые операторы (или перегруженные операторы) потерпят неудачу. Например. ast.literal_eval("1 & 1")выдаст ошибку но eval("1 & 1")не будет.
Даниэль ван Флаймен
1
Просто любопытно. Разве мы не должны использовать парсеры выражений или что-то подобное, если мы ожидаем что-то вроде «1 & 1»?
thelinuxer
@ thelinuxer ты все еще должен, да; вы просто не сможете использовать ast.literal_evalчто-то подобное (например, вы можете реализовать парсер вручную).
Волатильность
104

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

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

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

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

смеситель
источник
поддерживает байтовые строки (байты класса). Например. b'Hello World '
XChikuX
52

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

ast.literal_eval: Безопасный анализ узла выражения или строки, содержащей литерал Python или отображение контейнера. Предоставленная строка или узел может состоять только из следующих литеральных структур Python: строк, байтов, чисел, кортежей, списков, диктов, наборов, логических значений, 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возражать и functionвозражать от созданных экземпляров подклассов. Это альтернативный способ CPythonдоступа к подклассам объекта и присоединения системы.

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

Киран Кумар Котари
источник
1
Я использую Python 2.7, и я только что проверил его работоспособность на Python 3.x. Мое плохое, я продолжал пробовать это на
питоне
3
ast.literal_eval("1+1")не работает в Python 3.7 и, как уже было сказано, literal_eval должен быть ограничен литералами этих нескольких структур данных. Он не должен быть в состоянии анализировать двоичную операцию.
Сесшу
Не могли бы вы объяснить свой KABOOMкод, пожалуйста? Нашел здесь:KABOOM
winklerrr
3
@winklerrr KABOOMхорошо объясняется здесь: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Элайхас
41

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

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


Например, ввод этого в приглашении будет очень и очень плохим для вас:

__import__('os').system('rm -rf /a-path-you-really-care-about')
nneonneo
источник
3

Если все, что вам нужно, это пользовательский словарь, возможно лучшее решение json.loads. Основным ограничением является то, что для json требуется строковые ключи. Также вы можете предоставить только буквальные данные, но это также относится и к literal_eval.

Chinasaur
источник
1

Я застрял с ast.literal_eval(). Я пробовал это в отладчике IntelliJ IDEA, и он продолжал возвращаться Noneна выходе отладчика.

Но позже, когда я назначил ее вывод переменной и напечатал ее в коде. Работало нормально. Пример совместного использования кода:

import ast
sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]'
output_value = ast.literal_eval(sample_string)
print(output_value)

Его версия Python 3.6.

М Хазик
источник