各框架對策
Ruby on Rails 的資安對策 — 正式環境強化實務參考
Ruby on Rails 的正式環境強化參考:優先度清單,加上秘密與 credentials、設定、gem CVE、Strong Parameters、授權、注入與 SSRF。以防禦視角撰寫,不含攻擊步驟。
對象:正在維運 Ruby on Rails 應用程式的人。這裡不談攻擊步驟——這是用來強化的可實作參考:依優先度排序的清單、各領域指引與自我檢查。想看跨框架的全貌,請參閱 各框架資安對策的入口。
依優先度排序的強化清單
由上往下逐項處理這張表。P0 是前提,P1 是最頻繁的事故來源,P2 是持續性的維運衛生。
P0 ── 前提(先做這個)
秘密與 credentials 管理/正式環境設定(不外露例外、force_ssl)/迅速修補 gem CVE
P1 ── 最主要的事故來源
Strong Parameters/Mass Assignment 控制/授權(擁有者範圍)
P2 ── 維運衛生
注入與危險方法/工作階段、CSRF/SSRF、上傳
| 優先度 | 對策 | 具體(Rails) |
|---|---|---|
| P0 | 秘密與 credentials | 加密 credentials+分離的 master key。別 commit master key。secret_key_base 外洩時輪替 |
| P0 | 正式環境設定 | 不外露例外細節;config.force_ssl;用 filter_parameters 讓秘密不進入日誌 |
| P0 | gem(相依套件)CVE | 以 bundler-audit / osv-scanner 監控;以實際執行版本判定,迅速修補 |
| P1 | Strong Parameters | 把 permit 保持最小。別用 permit!。絕不賦值權限欄位 |
| P1 | 明確的授權 | 以 Pundit / CanCanCan+authorize+current_user 限定範圍確認擁有者 |
| P2 | 注入/危險方法 | 在 where 用綁定。別把輸入交給 send/constantize。別載入不可信的 YAML/Marshal |
| P2 | 工作階段/CSRF | protect_from_forgery(預設)、cookie secure/httponly/samesite、登入時重新產生 |
| P2 | SSRF/上傳 | 伺服器端取得採允許清單+阻擋內部 IP。驗證上傳,存到 public/ 之外 |
1. 秘密與 credentials(P0)
- 使用 Rails 的加密 credentials,並別把 master(解密)key commit 進 repo(改用環境變數等注入)。
secret_key_base是簽章/加密 cookie 與工作階段的基礎。外洩就立即輪替(注意這會使既有的簽章/加密失效)。- 別把
.env、傾印檔或備份放在公開目錄(→ 別把秘密放在公開目錄)。
2. 正式環境設定(P0)
- 在正式環境別外露例外細節(別啟用
consider_all_requests_local等)——減少內部結構的揭露。 - 用
config.force_ssl強制 HTTPS 並把 cookie 標記為 secure。別在正式環境啟用 web console 這類開發工具。 - 用
filter_parameters讓密碼等內容不進入日誌。
3. gem(相依套件)CVE(P0)
- 用 bundler-audit 或 osv-scanner 以機器監控 gem 的 CVE,以實際執行的版本(Gemfile.lock)判定,迅速修補(→ 監控相依套件的 CVE · 漏洞應變實務手冊)。
- 把 Rails 與 Ruby 保持在受支援的版本,別放任 EOL 版本。
4. Strong Parameters 與 Mass Assignment(P1)
- 把
permit欄位保持最小,避免permit!(全允許)。 - **絕不從使用者輸入賦值像
is_admin這類權限欄位。**丟掉「為了方便就放寬」。
5. 授權(P1——最主要的來源)
常見(危險)
- 不寫授權——「已登入=可檢視/更新」
- 路由模型綁定取到別的使用者的 ID
- 依賴隱藏的路由/難以猜測的 ID
- 有些 action 忘了寫
authorize
正確
- 以 Pundit / CanCanCan 把政策寫明確+每個 action 都
authorize - 連讀取也用
current_user限定為擁有者範圍 - 在每一條讀取/更新/刪除路徑上驗證
- 授權是明確地寫出來,而非交給預設
參閱 IDOR 是什麼。驗證與授權是兩回事;在貼近資料的地方授權。
6. 注入與危險的動態方法(P2)
- SQL:在
where以佔位符綁定;絕不用字串串接組查詢(避免where("... #{params}"))(→ SQL 注入是什麼)。 - 危險的動態方法:別把使用者輸入交給
send/public_send/constantize。若非用不可,就以允許清單嚴格限制。 - 還原序列化:避免載入不可信的
YAML/Marshal(在特定條件下可能導致程式碼執行)。
7. 工作階段、cookie、CSRF(P2)
- 別全域停用 CSRF 保護(
protect_from_forgery);善用表單 token(→ CSRF 是什麼)。 - 在 cookie/工作階段設 secure/httponly/samesite,並在登入時重新產生工作階段。
8. SSRF、上傳、標頭(P2)
- 伺服器端取得使用者提供的 URL 時,應把目標限制為允許清單並阻擋觸及內部 IP/中繼資料(→ SSRF 是什麼)。
- 驗證上傳(類型/大小)、存到
public/之外,並不授予執行權限。 - 加上安全標頭(用 安全標頭檢測 檢查自己的網站)。
檢驗:你的 Rails 真的強化了嗎?
做完不是終點——檢查過才算完成。以下是針對你自己環境的防禦性自我檢查。
秘密沒有外露/被 commit
master.key 不在 repo 裡,且 .env/傾印檔無法用 URL 取得。正式環境不外露例外細節
授權有效
相依套件與標頭
bundler-audit/osv 為乾淨,且透過 標頭檢測 確認有 HTTPS/HSTS。本站的觀點:即使有慣例守著,授權與相依套件仍在你身上
Rails 良好的預設消去了許多風險,但授權——誰可以做什麼——以及相依套件的新鮮度,是應用程式與維運特有的,框架無法自動替你守。我們反覆看到的事故,與其說是精巧的攻擊,不如說是「有驗證卻沒有擁有者檢查」那一型。所以防禦的重心,是把上面那張表由上往下逐一處理——收緊 Strong Parameters、把授權寫明確、對 gem 監控 CVE。
接下來讀
- 入口:各框架資安對策 · Laravel 資安對策(同為 MVC、授權的型態相近)
- 實務:漏洞應變實務手冊 · 監控相依套件的 CVE · 別把秘密放在公開目錄
- 名詞:IDOR 是什麼 · SQL 注入 · CSRF · SSRF
- 工具:安全標頭檢測
FAQ
Q要保護 Rails,第一步該做什麼?
三個 P0 項目:(1) 安全地處理秘密(把加密的 credentials 與 master key 分開,別把 master key commit 進去;secret_key_base 是簽章/加密 cookie 的基礎,外洩時要輪替);(2) 收緊正式環境設定(不外露例外細節;force_ssl);(3) 用機器監控 gem(相依套件)的 CVE 並迅速修補,以實際執行的版本判定。接著再進到 Strong Parameters 與授權。
Q使用 Strong Parameters 要注意什麼?
明確允許(permit)你接受哪些欄位,以防止 Mass Assignment(把未預期的欄位一次性批次賦值)。危險的是為了方便而放得太寬,以及用 permit! 允許一切。把允許的欄位收到最小限度,像 is_admin 這類權限欄位絕對不要讓它從使用者輸入被賦值。
Q要如何安全地實作授權?
在登入(驗證)之上,實作一個確認目標真的屬於該使用者的擁有者檢查。用 Pundit/CanCanCan 把政策寫明確,在每個 action 都執行 authorize,並用 current_user 限定資源範圍。別依賴隱藏的路由或難以猜測的 ID——在每一條讀取/更新/刪除的路徑上都明確驗證。
Qgem(相依套件)的漏洞要如何管理?
用 bundler-audit 或 osv-scanner 以機器監控已知 CVE 並迅速修補,以實際執行的版本判定(依 Gemfile.lock 的實際版本判斷)。把 Rails 與 Ruby 保持在受支援的版本,別放任 EOL 版本。相依套件的新鮮度,比起精巧的攻擊,是更貼近現實的事故分水嶺。
Q危險的動態方法有哪些?
把使用者輸入交給 send / public_send / constantize,可能導致未預期的方法呼叫或類別解析。同樣地,透過 Marshal 或不可信的 YAML 載入(還原物件),在特定條件下可能導致程式碼執行。別把使用者來源的資料交給這些方法;若非用不可,就以允許清單嚴格限制。