我遇到一些代码,其中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()
是唯一安全选项吗?
答案 0 :(得分:155)
datamap = eval(raw_input('Provide some data here: '))
表示您实际在之前评估代码,您认为它不安全。它会在调用函数后立即评估代码。另请参阅the dangers of eval
。
ast.literal_eval
会引发异常,因此如果代码不是,则不会执行代码。
只要您需要ast.literal_eval
,请使用eval
。您通常不应该评估文字Python语句。
答案 1 :(得分:86)
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()
。它安全地做你想要的,仅此而已。
答案 2 :(得分:40)
Python的渴望在其评估中,因此eval(raw_input(...))
会在用户输入eval
后立即评估用户的输入,无论您之后对数据做了什么。因此,这不安全,尤其是当您eval
用户输入时。
使用ast.literal_eval
。
例如,在提示符处输入此内容对您来说非常非常糟糕:
__import__('os').system('rm -rf /a-path-you-really-care-about')
答案 3 :(得分:35)
<强> EVAL:强>
这非常强大,但如果您接受要从不受信任的输入进行评估的字符串,这也非常危险。假设被评估的字符串是“os.system('rm -rf /')”?它会真正开始删除计算机上的所有文件。
的 ast.literal_eval:强>
安全地评估表达式节点或包含Python文字或容器显示的字符串。提供的字符串或节点可能只包含以下Python文字结构:字符串,字节,数字,元组,列表,字符串,集合,布尔值,无,字节和集合。
的语法:强>
eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)
示例:强>
# python 2.x - doesn't accepts operators in the string
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()现在更加严格。不再允许添加和减去任意数字。 link
答案 4 :(得分:3)
如果你需要的只是一个用户提供的词典,可能更好的解决方案是json.loads
。主要限制是json dicts需要字符串键。此外,您只能提供文字数据,但literal_eval
也是如此。
答案 5 :(得分: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版。
答案 6 :(得分:0)
在最近的 Python3 中,ast.literal_eval() 不再解析简单的字符串,而是应该使用 ast.parse() 方法来创建一个 AST,然后对其进行解释。
这是在 Python 3.6+ 中正确使用 ast.parse() 来安全评估简单算术表达式的完整示例。
import ast, operator, math
import logging
logger = logging.getLogger(__file__)
def safe_eval(s):
def checkmath(x, *args):
if x not in [x for x in dir(math) if not "__" in x]:
raise SyntaxError(f"Unknown func {x}()")
fun = getattr(math, x)
return fun(*args)
binOps = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
ast.Call: checkmath,
ast.BinOp: ast.BinOp,
}
unOps = {
ast.USub: operator.neg,
ast.UAdd: operator.pos,
ast.UnaryOp: ast.UnaryOp,
}
ops = tuple(binOps) + tuple(unOps)
tree = ast.parse(s, mode='eval')
def _eval(node):
if isinstance(node, ast.Expression):
logger.debug("Expr")
return _eval(node.body)
elif isinstance(node, ast.Str):
logger.debug("Str")
return node.s
elif isinstance(node, ast.Num):
logger.debug("Num")
return node.value
elif isinstance(node, ast.Constant):
logger.info("Const")
return node.value
elif isinstance(node, ast.BinOp):
logger.debug("BinOp")
if isinstance(node.left, ops):
left = _eval(node.left)
else:
left = node.left.value
if isinstance(node.right, ops):
right = _eval(node.right)
else:
right = node.right.value
return binOps[type(node.op)](left, right)
elif isinstance(node, ast.UnaryOp):
logger.debug("UpOp")
if isinstance(node.operand, ops):
operand = _eval(node.operand)
else:
operand = node.operand.value
return unOps[type(node.op)](operand)
elif isinstance(node, ast.Call):
args = [_eval(x) for x in node.args]
r = checkmath(node.func.id, *args)
return r
else:
raise SyntaxError(f"Bad syntax, {type(node)}")
return _eval(tree)
if __name__ == "__main__":
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
logger.addHandler(ch)
assert safe_eval("1+1") == 2
assert safe_eval("1+-5") == -4
assert safe_eval("-1") == -1
assert safe_eval("-+1") == -1
assert safe_eval("(100*10)+6") == 1006
assert safe_eval("100*(10+6)") == 1600
assert safe_eval("2**4") == 2**4
assert safe_eval("sqrt(16)+1") == math.sqrt(16) + 1
assert safe_eval("1.2345 * 10") == 1.2345 * 10
print("Tests pass")