摘要:如果一定要用的話,那么就需要注意一下下面這些安全相關(guān)的問題。全局變量和內(nèi)置函數(shù)在執(zhí)行的代碼中,默認(rèn)可以訪問執(zhí)行時(shí)的局部變量和全局變量,同樣也會(huì)修改全局變量。所以我們的檢查代碼可以這樣寫我所知道的使用函數(shù)時(shí)需要注意的安全問題就是這些了。
眾所周知,在 python 中可以使用 exec 函數(shù)來執(zhí)行包含 python 源代碼的字符串:
>>> code = """ ...: a = "hello" ...: print(a) ...: """ >>> exec(code) hello >>> a "hello"
exec 函數(shù)的這個(gè)功能很是強(qiáng)大,慎用。如果一定要用的話,那么就需要注意一下下面這些安全相關(guān)的問題。
全局變量和內(nèi)置函數(shù)在 exec 執(zhí)行的代碼中,默認(rèn)可以訪問執(zhí)行 exec 時(shí)的局部變量和全局變量, 同樣也會(huì)修改全局變量。如果 exec 執(zhí)行的代碼是根據(jù)用戶提交的數(shù)據(jù)生產(chǎn)的話,這種默認(rèn)行為就是一個(gè)安全隱患。
如何更改這種默認(rèn)行為呢?可以通過執(zhí)行 exec 函數(shù)的時(shí)候再傳兩個(gè)參數(shù)的方式來 修改這種行為(詳見 之前 關(guān)于 exec 的文章):
>>> g = {} >>> l = {"b": "world"} >>> exec("hello = "hello" + b", g, l) >>> l {"b": "world", "hello": "helloworld"} >>> g {"__builtins__": {...}} >>> hello --------------------------------------------------------------------------- NameError Traceback (most recent call last) ... NameError: name "hello" is not defined
如果要限制使用內(nèi)置函數(shù)的話,可以在 globals 參數(shù)中定義一下 __builtins__ 這個(gè) key:
>>> g = {} >>> l = {} >>> exec("a = int("1")", g, l) >>> l {"a": 1} >>> g = {"__builtins__": {}} >>> exec("a = int("1")", g, l) Traceback (most recent call last): File "", line 1, in File " ", line 1, in NameError: name "int" is not defined >>>
現(xiàn)在我們限制了訪問和修改全局變量以及使用內(nèi)置函數(shù),難道這樣就萬事大吉了嗎? 然而并非如此,還是可以通過其他的方式來獲取內(nèi)置函數(shù)甚至 os.system 函數(shù)。
另辟蹊徑獲取內(nèi)置函數(shù)和 os.system通過函數(shù)對象:
>>> def a(): pass ... >>> a.__globals__["__builtins__"] >>> a.__globals__["__builtins__"].open
通過內(nèi)置類型對象:
>>> for cls in {}.__class__.__base__.__subclasses__(): ... if cls.__name__ == "WarningMessage": ... b = cls.__init__.__globals__["__builtins__"] ... b["open"] ...>>>
獲取 os.system:
>>> cls = [x for x in [].__class__.__base__.__subclasses__() if x.__name__ == "_wrap_close"][0] >>> cls.__init__.__globals__["path"].os>>>
對于這兩種辦法又如何應(yīng)對呢? 一種辦法就是禁止訪問以 _ 開頭的屬性:
如果可以控制 code 的生成,那么就在生成 code 的時(shí)候判斷
如果不能的話,可以通過 dis 模塊分析生成的 code (dist 無法分析嵌套函數(shù)的代碼)
使用 tokenize 模塊:
In [68]: from io import BytesIO In [69]: code = """ ....: a = "b" ....: a.__str__ ....: def b(): ....: b.__get__ ....: """ In [70]: t = tokenize(BytesIO(code.encode()).readline) In [71]: for x in t: ....: print(x) ....: TokenInfo(type=59 (ENCODING), string="utf-8", start=(0, 0), end=(0, 0), line="") TokenInfo(type=58 (NL), string=" ", start=(1, 0), end=(1, 1), line=" ") TokenInfo(type=1 (NAME), string="a", start=(2, 0), end=(2, 1), line="a = "b" ") TokenInfo(type=53 (OP), string="=", start=(2, 2), end=(2, 3), line="a = "b" ") TokenInfo(type=3 (STRING), string=""b"", start=(2, 4), end=(2, 7), line="a = "b" ") TokenInfo(type=4 (NEWLINE), string=" ", start=(2, 7), end=(2, 8), line="a = "b" ") TokenInfo(type=1 (NAME), string="a", start=(3, 0), end=(3, 1), line="a.__str__ ") TokenInfo(type=53 (OP), string=".", start=(3, 1), end=(3, 2), line="a.__str__ ") TokenInfo(type=1 (NAME), string="__str__", start=(3, 2), end=(3, 9), line="a.__str__ ") TokenInfo(type=4 (NEWLINE), string=" ", start=(3, 9), end=(3, 10), line="a.__str__ ") TokenInfo(type=1 (NAME), string="def", start=(4, 0), end=(4, 3), line="def b(): ") TokenInfo(type=1 (NAME), string="b", start=(4, 4), end=(4, 5), line="def b(): ") TokenInfo(type=53 (OP), string="(", start=(4, 5), end=(4, 6), line="def b(): ") TokenInfo(type=53 (OP), string=")", start=(4, 6), end=(4, 7), line="def b(): ") TokenInfo(type=53 (OP), string=":", start=(4, 7), end=(4, 8), line="def b(): ") TokenInfo(type=4 (NEWLINE), string=" ", start=(4, 8), end=(4, 9), line="def b(): ") TokenInfo(type=5 (INDENT), string=" ", start=(5, 0), end=(5, 4), line=" b.__get__ ") TokenInfo(type=1 (NAME), string="b", start=(5, 4), end=(5, 5), line=" b.__get__ ") TokenInfo(type=53 (OP), string=".", start=(5, 5), end=(5, 6), line=" b.__get__ ") TokenInfo(type=1 (NAME), string="__get__", start=(5, 6), end=(5, 13), line=" b.__get__ ") TokenInfo(type=4 (NEWLINE), string=" ", start=(5, 13), end=(5, 14), line=" b.__get__ ") TokenInfo(type=6 (DEDENT), string="", start=(6, 0), end=(6, 0), line="") TokenInfo(type=0 (ENDMARKER), string="", start=(6, 0), end=(6, 0), line="")
從上面的輸出我們可以知道當(dāng) type 是 OP 并且 string 等于 "." 時(shí),下一條記錄就是
點(diǎn)之后的屬性名稱。所以我們的檢查代碼可以這樣寫:
import io import tokenize def check_unsafe_attributes(string): g = tokenize.tokenize(io.BytesIO(string.encode("utf-8")).readline) pre_op = "" for toktype, tokval, _, _, _ in g: if toktype == tokenize.NAME and pre_op == "." and tokval.startswith("_"): attr = tokval msg = "access to attribute "{0}" is unsafe.".format(attr) raise AttributeError(msg) elif toktype == tokenize.OP: pre_op = tokval
我所知道的使用 exec 函數(shù)時(shí)需要注意的安全問題就是這些了。 如果你還知道其他需要注意的安全問題的話,歡迎留言告知。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/11187.html
摘要:如果一定要用的話,那么就需要注意一下下面這些安全相關(guān)的問題。全局變量和內(nèi)置函數(shù)在執(zhí)行的代碼中,默認(rèn)可以訪問執(zhí)行時(shí)的局部變量和全局變量,同樣也會(huì)修改全局變量。所以我們的檢查代碼可以這樣寫我所知道的使用函數(shù)時(shí)需要注意的安全問題就是這些了。 眾所周知,在 python 中可以使用 exec 函數(shù)來執(zhí)行包含 python 源代碼的字符串: >>> code = ...: a = hel...
摘要:內(nèi)置函數(shù)們能夠被提拔出來,這就意味著它們皆有獨(dú)到之處,有用武之地。因此,掌握內(nèi)置函數(shù)的用法,就成了我們應(yīng)該點(diǎn)亮的技能。報(bào)錯(cuò)包含了內(nèi)置命名空間中的名稱,在控制臺中輸入,就能發(fā)現(xiàn)很多內(nèi)置函數(shù)異常和其它屬性的名稱。 Python 提供了很多內(nèi)置的工具函數(shù)(Built-in Functions),在最新的 Python 3 官方文檔中,它列出了 69 個(gè)。 大部分函數(shù)是我們經(jīng)常使用的,例如 p...
摘要:在本文中我們將解決一些用于生成的模板引擎需要面對的一些安全問題。整個(gè)系列的所有文章地址讓我們一起來構(gòu)建一個(gè)模板引擎一讓我們一起來構(gòu)建一個(gè)模板引擎二讓我們一起來構(gòu)建一個(gè)模板引擎三讓我們一起來構(gòu)建一個(gè)模板引擎四文章中涉及的代碼已經(jīng)放到上了 在 上篇文章 中我們的模板引擎實(shí)現(xiàn)了對 include 和 extends 的支持, 到此為止我們已經(jīng)實(shí)現(xiàn)了模板引擎所需的大部分功能。 在本文中我們將解...
摘要:相同點(diǎn)都可以獲得命令執(zhí)行的狀態(tài)碼作為一種服務(wù)器端的腳本語言,象編寫簡單,或者是復(fù)雜的動(dòng)態(tài)網(wǎng)頁這樣的任務(wù),它完全能夠勝任。于是的設(shè)計(jì)者們給加了一個(gè)門安全模式。第二個(gè)參數(shù)是可選的,用來得到命令執(zhí)行后的狀態(tài)碼。 詳細(xì)的介紹了關(guān)于PHP exec system passthru系統(tǒng)函數(shù)用法與安全以及其它應(yīng)用功能,有需要的朋友參考一下。區(qū)別:system() 輸出并返回最后一行shell結(jié)果。e...
摘要:共享資源臨界資源修飾實(shí)例方法輸出結(jié)果上述代碼與前面不同的是我們同時(shí)創(chuàng)建了兩個(gè)新實(shí)例,然后啟動(dòng)兩個(gè)不同的線程對共享變量進(jìn)行操作,但很遺憾操作結(jié)果是而不是期望結(jié)果。 線程安全是并發(fā)編程中的重要關(guān)注點(diǎn),應(yīng)該注意到的是,造成線程安全問題的主要誘因有兩點(diǎn) 一是存在共享數(shù)據(jù)(也稱臨界資源) 二是存在多條線程共同操作共享數(shù)據(jù) 因此為了解決這個(gè)問題,我們可能需要這樣一個(gè)方案,當(dāng)存在多個(gè)線程操作共享...
閱讀 2092·2023-04-25 20:52
閱讀 2473·2021-09-22 15:22
閱讀 2122·2021-08-09 13:44
閱讀 1766·2019-08-30 13:55
閱讀 2804·2019-08-23 15:42
閱讀 2278·2019-08-23 14:14
閱讀 2871·2019-08-23 13:58
閱讀 3002·2019-08-23 11:49