前幾天看到「10 Unknown Security Pitfalls for Python」這篇,講 Python ecosystem 裡面的一些設計造成的安全性問題,裡面很多東西都很有趣,而且有些算是共通的,其他程式語言也會中獎...
第一個是開發者用 assert()
確認權限,但 assert()
的設計是 debug 用的功能,所以可以預期在 optimization 後會被拿掉 (其他程式語言也有類似的功能),而導致權限沒被檢查而產生 security incident。
第二個是 os.makedirs()
所指定的 mode
,依照說明看起來是 Python 改變行為了,變成只有最終的那個目錄照著設定:
In Python < 3.6, the folders A, B and C are each created with permission 700. However, in Python > 3.6, only the last folder C has permission 700 and the other folders A and B are created with the default permission 755.
這個設計就有點討厭了,我測了一下 umask 077
的情況下在 shell 下執行 mkdir a/b/c
,所有的目錄都會是 700
。
第三個是串檔名的 os.path.join()
,遇到 /
開頭的部份會把前面的部份都拔掉,也就是這樣:(嗯... 這樣搞的嗎 XDDD)
>>> os.path.join('var', 'lib', 'tmp', 'abc.png')
'var/lib/tmp/abc.png'
>>> os.path.join('var', 'lib', 'tmp', '/etc/shadow')
'/etc/shadow'
第四個就是蠻標準的 variable injection 了。
第五個算是每次處理 .zip
檔都很頭痛的問題,不限於 Python,因為壓縮檔裡面可能會有 ../../
開頭這種看起來就很邪惡的檔案名稱,只要有安全意識的工程師都覺得處理起來很麻煩的東西...
第六個一看就是壞東西,想要用黑名單擋 injection 一定會有更多有創意的方法打進來。比較特別的是這邊有提到在 Python 裡 re.search()
與 re.match()
的差異,這點真的是寫起來有時後會忽略掉的...
第七與第八個都是 Unicode 相關的問題,也是個超級麻煩的東西,大原則是先 normalize 再 escape,但還是有點見招拆招去看...
第九個也是類似的問題,沒有先 normalize 再確認,不過一般機器都會有 private ip address,還是可以試著打... 這個有機會用架構面去解決,像是用 MQ 架設 proxy service,隔出另外一區來跑;或是已經有 zero trust 的架構,內部網路也是要帶 token 跑來跑去,可以減少一些傷害...
第十個算是 Python 自己的歷史因素,早期同時吃 &
與 ;
當作 separator,而非只吃 &
的 x-www-form-urlencoded
(HTML spec 內定義的標準):
In Python < 3.7 the function urllib.parse.parse_qsl allows the use of the ; and & characters as separators for URL query variables.
查了一下官方文件可以看到 3.10 之後才變成 x-www-form-urlencoded
:
Changed in version 3.10: Added separator parameter with the default value of &. Python versions earlier than Python 3.10 allowed using both ; and & as query parameter separator. This has been changed to allow only a single separator key, with & as the default separator.
有些應該是有安全意識時會去翻 spec 看看就可以避開的,但有些就真的是蠻雷的 XDDD