Python eval (компиляция (...), песочница), globals идут в песочнице, если только в def, почему?
Рассмотрим следующее:
def test(s):
globals()['a'] = s
sandbox = {'test': test}
py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"'
eval(compile(py_str, '<string>', 'exec'), sandbox)
'a' in sandbox # returns False, !What I dont want!
'b' in sandbox # returns True, What I want
'a' in globals() # returns True, !What I dont want!
'b' in globals() # returns False, What I want
Я даже не уверен, как спросить, но я хочу, чтобы глобальная область для функции была средой, которую я намереваюсь запустить, без необходимости компилировать функцию во время eval. Возможно ли это?
Спасибо за любой ввод
Решение
def test(s):
globals()['a'] = s
sandbox = {}
# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
test.func_closure)
# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest
py_str = 'test("Setting A")\nglobals()["b"] = "Setting B"'
eval(compile(py_str, '<string>', 'exec'), sandbox)
'a' in sandbox # returns True
'b' in sandbox # returns True
'a' in globals() # returns False
'b' in globals() # returns False
Ответы
Ответ 1
Когда вы вызываете функцию в Python, глобальные переменные, которые она видит, всегда являются глобальными значениями модуля, в котором он был определен. (Если это не так, функция может не работать - на самом деле, возможно, потребуются некоторые глобальные значения, и вы не обязательно знаете, что это такое.) Указание словаря глобальных символов с exec
или eval()
влияет только на глобальные переменные, которые видит код exec
'd или eval()
' d.
Если вы хотите, чтобы функция увидела другие глобальные переменные, вам действительно нужно включить определение функции в строку, переданную в exec
или eval()
. Когда вы это делаете, функция "module" - это строка, из которой она была скомпилирована, с ее собственными глобальными (то есть теми, которые вы предоставили).
Вы можете обойти это, создав новую функцию с тем же кодовым объектом, что и тот, который вы вызываете, но другой атрибут func_globals
, который указывает на ваш глобальный dict, но это довольно продвинутый хакер и, вероятно, не стоит, Тем не менее, вот как вы это сделаете:
# create a sandbox globals dict
sandbox = {}
# create a new version of test() that uses the sandbox for its globals
newtest = type(test)(test.func_code, sandbox, test.func_name, test.func_defaults,
test.func_closure)
# add the sandboxed version of test() to the sandbox
sandbox["test"] = newtest
Ответ 2
Контексты внешнего исполнения определяются статически в Python (f.func_globals
доступен только для чтения), поэтому я бы сказал, что то, что вы хотите, невозможно. Причина в том, что функция может стать недействительной Python, ее контекст определения изменяется во время выполнения. Если бы этот язык допустил это, это был бы очень простой путь для инъекции вредоносного кода в вызовы библиотеки.
def mycheck(s):
return True
exec priviledged_code in {'check_password':mycheck}
Ответ 3
Код песочницы для exec
, предоставляя альтернативные глобальные/локальные ресурсы, имеет много предостережений:
-
Альтернативные глобальные переменные /locals применяются только для кода в песочнице. Они не влияют ни на что иное, они не могут повлиять на что-либо вне его, и это не имело бы смысла, если бы они могли.
Другими словами, ваша так называемая "песочница" передает объект test
в код, исполняемый exec. Чтобы изменить глобальные переменные, которые видит test
, он также должен будет изменить объект, а не передавать его, как есть. Это никоим образом не может быть таким, чтобы это работало, а тем более в том, что объект будет продолжать делать что-то значимое.
-
Используя альтернативные глобальные переменные, все, что находится в sandbox
, все равно будет видеть встроенные функции. Если вы хотите скрыть некоторые или все встроенные функции из кода внутри песочницы, вам нужно добавить ключ "__builtins__"
в словарь, который указывает либо на None
(отключает все встроенные функции), либо на вашу версию. Это также ограничивает определенные атрибуты объектов, например, доступ к атрибуту func_globals
функции будет отключен.
-
Даже если вы удалите встроенные функции, песочница будет не безопасна. Песочница только код, которому вы доверяете в первую очередь.
Здесь простое доказательство понятия:
import subprocess
code = """[x for x in ().__class__.__bases__[0].__subclasses__()
if x.__name__ == 'Popen'][0](['ls', '-la']).wait()"""
# The following runs "ls"...
exec code in dict(__builtins__=None)
# ...even though the following raises
exec "(lambda:None).func_globals" in dict(__builtins__=None)