Python 的沙盒實驗
在 高可擴展的任務執行架構需求 一文中,山姆鍋提到基於 Python 的超輕量級沙盒 (sandbox)。沙盒是一個有限制的執行環境, 用來執行不受信任的程式碼 / 腳本 (script)。本文山姆鍋基於需要,實驗一個 Python 沙盒的開源專案。
請注意:按照該開源專案所述,不保證所提供的方法一定安全。
為什麼需要沙盒環境?
有時候應用軟體為了彈性起見,會允許使用者自己撰寫腳本來擴充軟體功能。由於不能預期使用者撰寫的內容,
如果沒有適當控制的話,小則讓程式崩潰,大則出現安全漏洞。為了解決這樣的問題,
有人提出讓不受信任的程式在一個沙盒環境中執行。就像小孩子玩沙一樣,只要不超出沙盒的範圍,
基本上不用擔心安全問題。
但要在 Python (精確講是 CPython
) 實作一個安全的沙盒環境並不是那麼簡單。使用 Python 內建的
eval
衍生的安全問題
眾所皆知
。
沙盒環境的需求
為了安全原因,沙盒中的程式允許執行的功能是越少越好。在
高可擴展的任務執行架構需求
文章所提的任務執行服務,其中代理人的邏輯便是由腳本實現。代理人的功能相對單純,只需要邏輯判斷跟呼叫平台提供的函式完成任務。
由於複雜跟不安全的操作都由平台提供,針對代理人的腳本,山姆鍋的需求只有:
- 支援運算式。
- 支援條件式、以及有限制的 for 迴圈流程控制。
除此之外的功能全部都要禁止使用!
沙盒環境的實作
上面提到使用 Python 的 eval
來執行不受信任的程式是非常危險的行為。那有沒有其他較安全的方案?
俗話說得好:「萬事問谷歌大神讓你變聰明」(誤)。不管如何,山姆鍋找到一個有希望的開源專案:
asteval 。
「工欲善其事,必先利其器」,讓我們進一步來了解 asteval
的工作原理。
asteval 的運作原理
asteval
的運作的基本原理如下:
- 使用 Python 內建的
ast
模組先將腳本剖析 (parse) 成抽象語法樹 (Abstract Syntax Tree ;
AST) 的形式, - 尋訪 (visit) 語法樹,只執行允許的功能。
感覺還蠻容易理解,對於要求安全性來說這是好事。容易理解才比較能看出哪裏有問題。
使用範例
底下是 asteval 網站提供的範例:
1 | from asteval import Interpreter |
延伸思考
- 雖然執行時期一定需要檢查,但在腳本儲存到系統之前似乎也可以先做一次大略的檢查。這樣在沒有實際執行腳本的情況下,就可以過濾掉大部份有問題的程式碼。
- 由於山姆鍋的需求比
asteval
提供的還少,除了原先它不允許的用法之外,還要進一步做限制。例如:
- 不支援 while 迴圈。
- 迴圈數不允許超過 100。
- 迴圈中不允許再包含迴圈。
- 不支援函式定義。
- 不允許 ‘**’ 運算子。
- 因為採用防堵的設計,總有可能遇到沒考慮到的情況導致出現漏洞。
結語
根據 asteval
網站提供的資訊,透過它來執行腳本比直接使用
eval
平均慢約 4 倍左右。
另外,這個沙盒環境沒有處理腳本消耗過多資源的問題,例如:‘2**1234567890’
這樣簡單的運算式就足夠讓腳本卡住。
參考資料
Abstract Syntax Tree:
https://en.wikipedia.org/wiki/Abstract_syntax_tree
抽象語法樹。