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

前端 未结 6 1236
情深已故
情深已故 2020-11-21 06:39

I have a situation with some code where eval() came up as a possible solution. Now I have never had to use eval() before but, I have come across p

相关标签:
6条回答
  • 2020-11-21 07:12

    Python's eager in its evaluation, so eval(input(...)) (Python 3) will evaluate the user's input as soon as it hits the eval, regardless of what you do with the data afterwards. Therefore, this is not safe, especially when you eval user input.

    Use ast.literal_eval.


    As an example, entering this at the prompt could be very bad for you:

    __import__('os').system('rm -rf /a-path-you-really-care-about')
    
    0 讨论(0)
  • 2020-11-21 07:13

    eval: This is very powerful, but is also very dangerous if you accept strings to evaluate from untrusted input. Suppose the string being evaluated is "os.system('rm -rf /')" ? It will really start deleting all the files on your computer.

    ast.literal_eval: Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, None, bytes and sets.

    Syntax:

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

    Example:

    # 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__':{}})
    

    In the above code ().__class__.__bases__[0] nothing but object itself. Now we instantiated all the subclasses, here our main enter code hereobjective is to find one class named n from it.

    We need to code object and function object from instantiated subclasses. This is an alternative way from CPython to access subclasses of object and attach the system.

    From python 3.7 ast.literal_eval() is now stricter. Addition and subtraction of arbitrary numbers are no longer allowed. link

    0 讨论(0)
  • 2020-11-21 07:17

    I was stuck with ast.literal_eval(). I was trying it in IntelliJ IDEA debugger, and it kept returning None on debugger output.

    But later when I assigned its output to a variable and printed it in code. It worked fine. Sharing code example:

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

    Its python version 3.6.

    0 讨论(0)
  • 2020-11-21 07:23

    If all you need is a user provided dictionary, possible better solution is json.loads. The main limitation is that json dicts requires string keys. Also you can only provide literal data, but that is also the case for literal_eval.

    0 讨论(0)
  • 2020-11-21 07:24

    datamap = eval(input('Provide some data here: ')) means that you actually evaluate the code before you deem it to be unsafe or not. It evaluates the code as soon as the function is called. See also the dangers of eval.

    ast.literal_eval raises an exception if the input isn't a valid Python datatype, so the code won't be executed if it's not.

    Use ast.literal_eval whenever you need eval. You shouldn't usually evaluate literal Python statements.

    0 讨论(0)
  • 2020-11-21 07:33

    ast.literal_eval() only considers a small subset of Python's syntax to be valid:

    The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

    Passing __import__('os').system('rm -rf /a-path-you-really-care-about') into ast.literal_eval() will raise an error, but eval() will happily delete your files.

    Since it looks like you're only letting the user input a plain dictionary, use ast.literal_eval(). It safely does what you want and nothing more.

    0 讨论(0)
提交回复
热议问题