程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Deep analysis of eval() and exec() in Python

編輯:Python

1、eval The basic usage of
grammar :eval(expression, globals=None, locals=None)

It has three parameters , among expression Is an expression or code object of type string , Used to do calculations ;globals And locals Is an optional parameter , The default value is None.

To be specific ,expression It can only be a single expression , Complex code logic is not supported , For example, assignment operation 、 Loop statements, etc .(PS: A single expression does not mean “ Simple and harmless ”, See... Below 4 section )

globals Used to specify the global namespace of the runtime , The type is dictionary , By default, the built-in namespace of the current module is used .locals Specifies the local namespace of the runtime , The type is dictionary , Use... By default globals Value . When both default , Then follow eval The scope of function execution . It is worth noting that , These two don't represent the real namespace , It works only in arithmetic , After the calculation, destroy .

x = 10
def func():
y = 20
a = eval('x + y')
print('a: ', a)
b = eval('x + y', {'x': 1, 'y': 2})
print('x: ' + str(x) + ' y: ' + str(y))
print('b: ', b)
c = eval('x + y', {'x': 1, 'y': 2}, {'y': 3, 'z': 4})
print('x: ' + str(x) + ' y: ' + str(y))
print('c: ', c)
func()

Output results :

a: 30
x: 10 y: 20
b: 3
x: 10 y: 20
c: 4

thus it can be seen , When a namespace is specified , Variable will look up... In the corresponding namespace . and , Their values do not override the values in the actual namespace .

2、exec The basic usage of
grammar :exec(object[, globals[, locals]])

stay Python2 in exec It's a statement , and Python3 Transform it into a function , image print equally .exec() And eval() Highly similar , The meaning and function of the three parameters are similar .

The main difference is ,exec() The first argument of is not an expression , It's a code block , This means two things : First, it cannot evaluate an expression and return it , Second, it can execute complex code logic , Relatively more powerful , for example , When a new variable is assigned to a contemporary code block , This variable may Survive in a namespace outside of a function .

>>> x = 1
>>> y = exec('x = 1 + 1')
>>> print(x)
>>> print(y)
2
None

It can be seen that ,exec() Internal and external namespaces are interlinked , Variables are passed out , Not like eval() function , You need a variable to receive the execution result of the function .

3、 Some details
Both functions are powerful , They execute the string contents as valid code . This is a string driven event , Is of great significance . However , In actual use , There are a lot of small details , Here is a list of what I know .

Common use : Convert the string to the corresponding object , for example string Turn into list ,string Turn into dict,string turn tuple wait .

>>> a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]"
>>> print(eval(a))
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]
>>> a = "{'name': 'Python cat ', 'age': 18}"
>>> print(eval(a))
{'name': 'Python cat ', 'age': 18}
# And eval It's a little different
>>> a = "my_dict = {'name': 'Python cat ', 'age': 18}"
>>> exec(a)
>>> print(my_dict)
{'name': 'Python cat ', 'age': 18}

eval() The return value of the function is expression The results of the implementation of , In some cases , It would be None, For example, if the expression is print() sentence , Or list append() In operation , The result of this kind of operation is None, therefore eval() The return value of will also be None.

>>> result = eval('[].append(2)')
>>> print(result)
None

exec() The return value of the function will only be None, Not related to the result of executing statement , therefore , take exec() Function assignment out , There is no need . In the executed statement , If you include return or yield , The value they produce cannot be in exec The outside of the function works .

>>> result = exec('1 + 1')
>>> print(result)
None

Of the two functions globals and locals Parameters , It's a white list , By defining the scope of the namespace , Prevent data in scope from being abused .

conpile() Function compiled code object , Can be used as eval and exec The first parameter of .

Quirky local namespace : We talked about exec() The variables in the function can change the original namespace , But there are exceptions .

def foo():
exec('y = 1 + 1\nprint(y)')
print(locals())
print(y)
foo()

According to the previous understanding , The expected result is that variables are stored in local variables y, So both prints will be 2, But the actual result is :

2
{'y': 2}
Traceback (most recent call last):
...( Omit some error messages )
print(y)
NameError: name 'y' is not defined

You can see that there are variables in the local namespace y, Why is it wrong to say it's undefined ?

Reason and Python The compiler of , For the above code , The compiler will first foo The function resolves to a ast( Abstract syntax tree ), Then all variable nodes are stored in the stack , here exec() The argument to is just a string , The whole thing is constant , Not executed as code , therefore y There is no such thing as . Until the second print() when , This is the first time that a variable y , But because there is no complete definition , therefore y Will not be stored in the local namespace .

In operation period ,exec() Function dynamically creates a local variable y , However, due to the Python The implementation mechanism of “ The local namespace of the runtime cannot be changed ”, That is to say, at this time y Can never be a member of a local namespace , When executed print() It's a mistake .

If you want to exec() After execution y Take it out , It can be like this :z = locals()['y'] , However, if you accidentally write the following code , May be an error :

def foo():
exec('y = 1 + 1')
y = locals()['y']
print(y)
foo()
# Report errors :KeyError: 'y'
# Put variables y Change to other variables and no error will be reported

KeyError It means that there is no corresponding key . In this case y Made a statement , But the assignment cannot be completed because of circular reference , namely key Value corresponding value It's an invalid value , So I can't read , It's a mistake .

And in this case 4 Varieties , I want to explain them in a self-contained way , But tried for a long time , failed . Let's leave a message , Wait for me to understand , Write a separate article .

4、 Why use it with caution eval() ?
Many dynamic programming languages have eval() function , It has the same effect , however , Without exception , People will tell you that , Avoid using it .

Why use it with caution eval() Well ? Mainly for safety , For untrusted data sources ,eval Functions are likely to cause code injection problems .

>>> eval("__import__('os').system('whoami')")
desktop-fa4b888\pythoncat
>>> eval("__import__('subprocess').getoutput('ls ~')")
# The result is a little , The content is the file information of the current path

In the example above , My privacy data is exposed . And what's more terrible is , If you change the order to rm -rf ~ , All files in the current directory will be deleted .

For the above example , There is a way to limit , Designated globals by {'__builtins__': None} perhaps {'__builtins__': {}} .

>>> s = {'__builtins__': None}
>>> eval("__import__('os').system('whoami')", s)
# Report errors :TypeError: 'NoneType' object is not subscriptable

builtins Contains names from built-in namespaces , Enter in the console dir(__builtins__) , You can find a lot of built-in functions 、 Names of exceptions and other properties . By default ,eval Functional globals Parameters will implicitly carry __builtins__ , Even if it is to make globals Parameter is {} So it is , So if you want to disable it , You have to explicitly specify its value .

The example above maps it to None, It means to limit eval The available built-in namespace is None, This limits the ability of expressions to call built-in modules or properties .

however , This method is not foolproof , Because there's still a way to attack .

A vulnerability mining expert shared an idea in his blog , It's eye opening . The core code is the following , You can try to execute , See what the output is .

>>> ().__class__.__bases__[0].__subclasses__()

There is also a blog , Not only mentioned are the means of the above example , It also provides a new way of thinking :

Warning : Never execute the following code , Consequence conceit .

>>> eval('(lambda fc=(lambda n: [c 1="c" 2="in" 3="().__class__.__bases__[0" language="for"][/c].__subclasses__() if c.__name__ == n][0]):fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})())()', {"__builtins__":None})

This line of code will result in Python direct crash fall

Except for hackers , Simple content can also attack . Write like this , The computing resources of the server will be exhausted in a short time .

>>> eval("2 ** 888888888", {"__builtins__":None}, {})

As mentioned above , We showed... Intuitively eval() The harm of function , However , Even if it's Python The experts use , There's no guarantee that it won't go wrong .

In official dumbdbm Module , once (2014 year ) Find a security hole , The attacker forges the database file , You can call eval() And attack .

Similarly, , In the last month (2019.02), There are core developers for Python 3.8 It also raises a security issue , The proposal is not in logging.config Use in eval() function , At present, the problem is still open state .

All this , It's enough to explain why we should use it carefully eval() 了 . The same is true ,exec() Functions should also be used with caution .

5、 A safe alternative
Since there are all kinds of security risks , Why create these two built-in methods ? Why use them ?

The reason is simple , because Python It's a flexible dynamic language . Different from static language , Dynamic language supports dynamic code generation , For projects that have been deployed , You can also make only a small partial modification , To realize bug Repair .

There is no way to use them relatively safely ?

ast Modular literal() yes eval() A safe alternative to , And eval() There are different ways to perform without checking ,ast.literal() We will first check whether the expression content is valid and legal . The literal content it allows is as follows :

strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None

Once the content is illegal , May be an error :

import ast
ast.literal_eval("__import__('os').system('whoami')")
Report errors :ValueError: malformed node or string

however , It also has disadvantages :AST Compiler stack depth (stack depth) Co., LTD. , When the parsed string content is too much or too complex , May cause the program to crash .

as for exec() , There seems to be no such alternative , After all, the content it can support is more complex and diverse .

Finally, a suggestion : Find out their differences and operation details ( For example, the previous local namespace content ), Use caution , Restrict available namespaces , Fully verify the data source .

The above is all the content shared this time , Want to know more python Welcome to official account :Python Programming learning circle , send out “J” Free access to , Daily dry goods sharing


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved