Ruby 安全性

Ruby 程式語言龐大而複雜,新手和經驗豐富的 Rubyist 一樣常常會遇到許多安全陷阱。

本文件旨在討論這些陷阱中的許多問題,並在適用時提供更安全的替代方案。

請查看完整的已知 CVE 列表,以及如何正確報告安全漏洞,網址為:www.ruby-lang.org/en/security/ 日本語版網址為:www.ruby-lang.org/ja/security/

安全

Marshal.load

Marshal 模組提供了將 Ruby 物件樹序列化和反序列化為二進制數據格式的方法。

永遠不要使用 Marshal.load 來反序列化不受信任或使用者提供的數據。因為 Marshal 可以將數據反序列化為幾乎任何 Ruby 物件,並對實例變量具有完全控制權,因此有可能製作一個惡意有效負載,該有效負載在反序列化後不久執行代碼。

如果您需要反序列化不受信任的數據,應該使用 JSON,因為它只能返回 '基本' 類型,如字符串、數組、哈希、數字和 nil。如果您需要反序列化其他類,則應該手動處理。永遠不要反序列化為使用者指定的類。

YAML

YAML 是一種流行的人類可讀數據序列化格式,被許多 Ruby 程序用於配置和數據庫持久化 Ruby 物件樹。

Marshal 類似,它能夠反序列化為任意 Ruby 類。例如,以下YAML數據在反序列化時將創建一個 ERB 對象

!ruby/object:ERB
src: puts `uname`

由於這個原因,許多適用於 Marshal 的安全注意事項也適用於 YAML。不要使用 YAML 反序列化不受信任的數據。

符號

符號通常被視為簡單字符串的語法糖,但它們發揮了更加關鍵的作用。MRI Ruby 實現內部使用符號來表示方法、變量和常量名。這是因為符號只是帶有名稱的整數,所以在哈希表中查找速度更快。

從 2.2 版開始,大多數符號都可以進行垃圾回收;這些被稱為 可收集的 符號。您創建的大多數符號(例如通過調用 to_sym)都是可收集的。

另一方面,不可收集的 符號永遠不會被垃圾回收。它們在修改代碼時創建

未更新且仍在調用 'SYM2ID' 的 C 擴展將創建不可收集的符號。在 2.2.0 版中存在的錯誤:`send` 和 `__send__` 也創建了不可收集的符號,使用關鍵字參數調用方法也可能創建一些符號。

不要從使用者輸入創建不可收集的符號。否則,這將允許使用者通過向應用程序洪水般地提供獨特字符串來發動拒絕服務攻擊,從而導致內存無限增長,直到 Ruby 進程被殺死或導致系統變得極其緩慢。

儘管使用者輸入可能不是一個好主意,但以前容易受到威脅的方法,如to_symrespond_to?methodinstance_variable_getconst_get等,現在已不再是威脅。

正規表達式

Ruby的正規表達式語法與其他語言相比有一些小的差異。在Ruby中,^$錨點不是指字符串的開頭和結尾,而是指的開頭和結尾。

這意味著如果您使用像/^[a-z]+$/這樣的正規表達式來限制字符串只包含字母,攻擊者可以通過傳遞包含一個字母、然後是一個換行符、然後是任意字符串的字符串來繞過此檢查。

如果您想在Ruby中匹配整個字符串的開頭和結尾,請使用錨點\A\z

eval

絕不要將不受信任或由用戶控制的輸入傳遞給eval

除非您正在實現類似irbpry的REPL,否則eval幾乎肯定不是您想要的。不要在將其傳遞給eval之前嘗試過濾用戶輸入-這種方法充滿了危險,很可能會使您的應用程序面臨嚴重的遠程代碼執行漏洞。

send

Ruby中的‘全局函數’(如putsexit等)實際上是Object上的私有實例方法。這意味著即使對send的調用具有明確的接收者,也可以使用send調用這些方法。

例如,以下代碼片段將“Hello world”寫入終端

1.send(:puts, "Hello world")

您絕不能將用戶提供的輸入作為第一個參數調用send。這樣做可能會引入拒絕服務漏洞

foo.send(params[:bar]) # params[:bar] is "exit!"

如果攻擊者可以控制send的前兩個參數,則可能發生遠程代碼執行

# params is { :a => "eval", :b => "...ruby code to be executed..." }
foo.send(params[:a], params[:b])

在根據用戶輸入分派方法調用時,請仔細驗證方法名。如果可能,請將其與安全方法名的白名單進行比對。

請注意,使用public_send也是危險的,因為send本身是公共的

1.public_send("send", "eval", "...ruby code to be executed...")

DRb

由於DRb允許遠程客戶端調用任意方法,因此不適合向不受信任的客戶端公開。

使用DRb時,請儘量避免在網絡上公開它。如果這不可行,並且您需要向世界公開DRb,則必須使用DRb::ACL配置適當的安全策略。