232

我有一些代码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()安全的选择吗?

4

7 回答 7

245

datamap = eval(input('Provide some data here: '))意味着您在认为代码不安全之前实际评估了代码。它会在调用函数后立即评估代码。另请参阅的危险eval

ast.literal_eval如果输入不是有效的 Python 数据类型,则引发异常,因此如果不是,则不会执行代码。

ast.literal_eval需要时使用eval。您通常不应该评估文字 Python 语句。

于 2013-03-04T08:52:53.967 回答
135

ast.literal_eval()仅认为 Python 语法的一小部分是有效的:

提供的字符串或节点只能由以下 Python 文字结构组成:字符串、字节、数字、元组、列表、字典、集合、布尔值和None.

传入__import__('os').system('rm -rf /a-path-you-really-care-about')ast.literal_eval()引发错误,但eval()会很高兴地删除您的文件。

因为看起来你只是让用户输入一个普通的字典,所以使用ast.literal_eval(). 它安全地做你想做的事,仅此而已。

于 2013-03-04T08:54:35.553 回答
70

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 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() 现在更严格了。不再允许任意数字的加减法。关联

于 2016-01-20T15:56:33.030 回答
47

Python急于评估,因此eval(input(...))(Python 3)将在用户输入点击 时立即评估用户的输入eval,而不管您之后如何处理数据。因此,这是不安全的,尤其是当您进行eval用户输入时。

使用ast.literal_eval.


例如,在提示符下输入它可能对您非常不利:

__import__('os').system('rm -rf /a-path-you-really-care-about')
于 2013-03-04T08:52:44.353 回答
6

如果您只需要用户提供的字典,则可能更好的解决方案是json.loads. 主要限制是 json dicts 需要字符串键。此外,您只能提供文字数据,但literal_eval.

于 2017-08-02T16:42:15.263 回答
6

在最近的 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")
于 2021-08-10T19:34:17.510 回答
2

我被困住了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版。

于 2018-10-02T07:34:25.443 回答