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
)都是可收集的。
另一方面,不可收集的 符號永遠不會被垃圾回收。它們在修改代碼時創建
-
定義一個方法(例如使用
define_method
), -
設置一個實例變量(例如使用
instance_variable_set
), -
創建一個變量或常量(例如使用
const_set
)
未更新且仍在調用 'SYM2ID' 的 C 擴展將創建不可收集的符號。在 2.2.0 版中存在的錯誤:`send` 和 `__send__` 也創建了不可收集的符號,使用關鍵字參數調用方法也可能創建一些符號。
不要從使用者輸入創建不可收集的符號。否則,這將允許使用者通過向應用程序洪水般地提供獨特字符串來發動拒絕服務攻擊,從而導致內存無限增長,直到 Ruby 進程被殺死或導致系統變得極其緩慢。
儘管使用者輸入可能不是一個好主意,但以前容易受到威脅的方法,如to_sym
、respond_to?
、method
、instance_variable_get
、const_get
等,現在已不再是威脅。
正規表達式¶ ↑
Ruby的正規表達式語法與其他語言相比有一些小的差異。在Ruby中,^
和$
錨點不是指字符串的開頭和結尾,而是指行的開頭和結尾。
這意味著如果您使用像/^[a-z]+$/
這樣的正規表達式來限制字符串只包含字母,攻擊者可以通過傳遞包含一個字母、然後是一個換行符、然後是任意字符串的字符串來繞過此檢查。
如果您想在Ruby中匹配整個字符串的開頭和結尾,請使用錨點\A
和\z
。
eval
¶ ↑
絕不要將不受信任或由用戶控制的輸入傳遞給eval
。
除非您正在實現類似irb
或pry
的REPL,否則eval
幾乎肯定不是您想要的。不要在將其傳遞給eval
之前嘗試過濾用戶輸入-這種方法充滿了危險,很可能會使您的應用程序面臨嚴重的遠程代碼執行漏洞。
send
¶ ↑
Ruby中的‘全局函數’(如puts
、exit
等)實際上是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
配置適當的安全策略。