eval()、exec()與execfile(),evalexecexecfile

來源:互聯網
上載者:User

eval()、exec()與execfile(),evalexecexecfile

eval(expression[, globals[, locals]])

eval()函數執行一個python運算式字串並返回運算式執行後的結果:

>>> x = 1>>> eval('x + 1')2

expression參數為一個運算式字串,globalslocals為可選的參數,globals必須是一個字典對象,locals可為任意映射對象,分別用作代碼執行的全域和局部命名空間。

globalslocals參數缺失的時候,運算式會使用當前環境的全域和局部命名空間值:

>>> x = 1>>> g = {'x' : 2}>>> eval('x + 1', g)3>>> eval('x + 1')2

eval()函數在資料類型轉換中很有用,可以將字串轉換成字典、列表等:

>>> exp = '[1,2,3,4]'>>> eval(exp)[1, 2, 3, 4]>>> exp = '{"a" : 1}'>>> eval(exp){'a': 1}

eval()也可以使用模組:

>>> import os>>> eval('os.getcwd()')'/home/user'

當然,eval不能直接對模組進行操作,如果非要使用eval進行import:

>>> eval('__import__("os").getcwd()')'/home/user'

這一般用於根據客戶需求動態調用不同的模組。

其它的用做計算機啥的也不在話下,不過eval函數也不能濫用,比如要擷取使用者的輸入並求值eval(raw_input()),

這種情況下,使用者輸入open(__file__).read()可直接把原檔案讀出來了。

要安全的使用eval()函數,可以使用globals和locals兩個參數來設定白名單,當參數缺失的時候,運算式會使用當前環境的全域和局部命名空間值,即globals()和locals()中包含的模組和函數:

>>> import os>>> 'os' in globals()True>>> eval('os.getcwd()')'/home/user'

將globals和locals兩個參數設定為空白:

>>> import os>>> eval('os.getcwd()', {}, {})Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<string>", line 1, in <module>NameError: name 'os' is not defined

但使用內建函數卻可以繞過白名單:

>>> eval('abs(10)', {}, {})10>>> eval('__import__("os").getcwd()', {}, {})'/home/user'

可以看下globals():

>>> globals(){'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}

__builtins__設定著python的內建函數,下面的寫法是一樣的:

>>> abs(10)10>>> __builtins__.abs(10)10

將__builtins__設定為空白則可避免內建函數的濫用:

>>> eval('abs(10)', {'__builtins__' : None}, {})Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<string>", line 1, in <module>NameError: name 'abs' is not defined 

 看情況這下是安全了,但是還是可以繞過的:

>>> ().__class__.__bases__[0].__subclasses__()[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'sys.getwindowsversion'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'nt.stat_result'>, <type 'nt.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'functools.partial'>, <type 'MultibyteCodec'>, <type 'MultibyteIncrementalEncoder'>, <type 'MultibyteIncrementalDecoder'>, <type 'MultibyteStreamReader'>, <type 'MultibyteStreamWriter'>, <type 'time.struct_time'>, <type '_ssl._SSLContext'>, <type '_ssl._SSLSocket'>, <type 'cStringIO.StringO'>, <type 'cStringIO.StringI'>, <class 'socket._closedsocket'>, <type '_socket.socket'>, <type 'method_descriptor'>, <class 'socket._socketobject'>, <class 'socket._fileobject'>, <type '_thread._localdummy'>, <type 'thread._local'>, <type 'thread.lock'>, <type 'collections.deque'>, <type 'deque_iterator'>, <type 'deque_reverse_iterator'>, <type 'itertools.combinations'>, <type 'itertools.combinations_with_replacement'>, <type 'itertools.cycle'>, <type 'itertools.dropwhile'>, <type 'itertools.takewhile'>, <type 'itertools.islice'>, <type 'itertools.starmap'>, <type 'itertools.imap'>, <type 'itertools.chain'>, <type 'itertools.compress'>, <type 'itertools.ifilter'>, <type 'itertools.ifilterfalse'>, <type 'itertools.count'>, <type 'itertools.izip'>, <type 'itertools.izip_longest'>, <type 'itertools.permutations'>, <type 'itertools.product'>, <type 'itertools.repeat'>, <type 'itertools.groupby'>, <type 'itertools.tee_dataobject'>, <type 'itertools.tee'>, <type 'itertools._grouper'>, <class 'threading._Verbose'>, <class 'string.Template'>, <class 'string.Formatter'>, <type 'CArgObject'>, <type '_ctypes.CThunkObject'>, <type '_ctypes._CData'>, <type '_ctypes.CField'>, <type '_ctypes.DictRemover'>, <type 'Struct'>, <class 'ctypes.CDLL'>, <class 'ctypes.LibraryLoader'>, <type 'cPickle.Unpickler'>, <type 'cPickle.Pickler'>, <class 'idlelib.rpc.SocketIO'>, <class 'idlelib.rpc.RemoteObject'>, <class 'idlelib.rpc.RemoteProxy'>, <class 'idlelib.rpc.RPCProxy'>, <class 'idlelib.rpc.MethodProxy'>, <type '_io._IOBase'>, <type '_io.IncrementalNewlineDecoder'>, <class 'subprocess.Popen'>, <class 'webbrowser.BaseBrowser'>, <class 'idlelib.tabbedpages.Page'>, <class 'idlelib.EditorWindow.HelpDialog'>, <type '_hashlib.HASH'>, <type '_random.Random'>, <class 'idlelib.EditorWindow.EditorWindow'>, <class 'idlelib.EditorWindow.IndentSearcher'>, <class 'idlelib.run.Executive'>]

通過tuple的class找到它的基類,也就是object,然後再找到object的各種子類,從中可以看到很多模組。

使用Quitter退出解譯器:

>>> eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__== 'Quitter'][0](0)()", {'__builtins__':None})user:~$ 

configobj,urllib,urllib2,setuptools等模組中都有os模組的內建:

>>> import configobj>>> 'os' in configobj.__dict__True>>> import urllib2>>> 'os' in urllib2.__dict__True>>> import setuptools>>> 'os' in setuptools.__dict__True

使用zipimport通過egg檔案匯入這些模組就可以使用os模組了:

eval("[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'zipimporter'][0]('/path/to/configobj-5.0.5-py2.7.egg').load_module('configobj').os.getcwd()", {'__builtins__':None})

以上可以看出,eval()函數的漏洞還是很多的,如果只是用來做類型轉換,可以使用ast.literal_eval 代替eval:

>>> import ast>>> ast.literal_eval('[1, 2, 3]')[1, 2, 3]>>> ast.literal_eval('abs(10)')Traceback (most recent call last):  File "<pyshell#12>", line 1, in <module>    ast.literal_eval('abs(10)')  File "C:\Python27\lib\ast.py", line 80, in literal_eval    return _convert(node_or_string)  File "C:\Python27\lib\ast.py", line 79, in _convert    raise ValueError('malformed string')ValueError: malformed string>>> eval('abs(10)')10

 exec(str [, globals[, locals]])

 execfile(filename [, globals[, locals]])

類似的,exec函數執行一個包含python代碼的字串,execfile則執行一個檔案,後兩個參數與eval類似。

>>> a = [1, 2, 3, 4]>>> exec('for i in a: print i')
activate_this = '/path/to/env/bin/activate_this.py'execfile(activate_this, dict(__file__=activate_this))

給eval或者exec函數傳遞字串時,解析器首先會把字串編譯成位元組碼,為避免消耗資源,可以使用compile函數將字串先行編譯:

compile(str, filename, kind)

str是要先行編譯的字串,filename為字串所在的檔案,kind參數為single時代表一條語句,exec代表一組語句,eval代表一個運算式

>>> s = 'for i in [1, 2, 3]: print i'>>> c = compile(s, '', 'exec')>>> exec(c)123>>> s2 = '1+1'>>> c2 = compile(s2, '', 'eval')>>> eval(c2)2

eval執行exec類型的先行編譯位元組碼時,會返回None:

>>> s2 = '1+1'>>> c2 = compile(s2, '', 'exec')>>> print eval(c2)None

 以前看到過一個案例,使用eval轉換不規則的json資料:

>>> blog = "{url : 'www.example.com'}"

上面url缺少引號,使用eval和json.loads()均會報錯。

>>> eval(blog)Traceback (most recent call last):  File "<pyshell#27>", line 1, in <module>    eval(blog)  File "<string>", line 1, in <module>NameError: name 'url' is not defined

使用eval時,eval會將url視為變數名並試圖在globals和locals中尋找url的值,添加一下globals參數:

>>> eval(blog, {'url' : 'url'}){'url': 'www.example.com'}

所以,現在要做的是就是建立一個類dict的映射對象,其中value的值需要與key值相同,可以使用type函數來建立:

>>> ValueFromKey = type('ValueFromKey', (dict,), dict(__getitem__ = lambda self,k : k))>>> eval(blog, ValueFromKey()){'url': 'www.example.com'}

在建立的type對象基類元祖中包含下dict,然後在對象的dict中定義一下__getitem__方法,使value值與key值一致即可。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.