Rails 常見資安陷阱與解決方法
WriterShelf™ is a unique multiple pen name blogging and forum platform. Protect relationships and your privacy. Take your writing in new directions. ** Join WriterShelf**
WriterShelf™ is an open writing platform. The views, information and opinions in this article are those of the author.
Article info
This article is part of:
Categories:
Tags:
Total: 2471 words
Like
or Dislike
More to explore
俗話說沒有安全就沒有一切,若在網站開發的同時能多注意資訊安全,日後可大幅降低補救資安漏洞所造成的維護成本。這篇是我初學Rails找到與資訊安全常見問題與解決方法,原文作者Ilya Bodrov,篇名為Common Rails Security Pitfalls and Their Solutions,希望對入門者有所幫助。若有錯誤請不吝指正。
Rails框架已經盡力做到資訊安全,然而依照官方文件所述,沒有所謂「隨插即用的資訊安全」,這篇文章討論其中幾個資安議題與保護步驟。
本篇涵蓋主題包括:
大量賦值(Mass assignment)
這大概是最常見的資安陷阱。大量賦值問題在於駭客嘗試一次更新資料庫多個欄位,其中包含了不應變更的欄位。
例如一個可以編輯使用者資料的表單如下:
然後controller更新動作如下:
看來好像沒甚麼問題,但實際上卻隱藏資安漏洞。如果users資料表包含admin欄位,駭客只要手動加入隱藏欄位像這樣:
從此駭客就擁有admin權限了。Rails4以後引入strong parameters以便指定那些欄位允許變動:
從此就只有name和surname欄位資料可以被變動。
跨網站腳本攻擊(XSS attacks)
除非你的網站是只有內部少數人在使用,否則別相信使用者送出的資料。自從Rails3版本以後,除非使用html_safe方法,否則在views回傳的字串資料皆會escape處理過。使用html_safe方法要很小心:
如果你的網頁允許使用者張貼文章,駭客可能會嘗試插入JavaScript在文章裡面隨者其他腳本一起執行,可能只是顯示一些alert訊息視窗,或惡意竊取使用者密碼等等。
所以絕對不要使用html_safe方法顯示使用者輸入的資料,改用sanitize方法指定可使用的標籤,通常是b或i:
有個Sanitize gem提供更多進階使用方式以解決此問題。
執行任意程式碼(Executing arbitrary code)
在使用如ActiveSupport::Inflector模組或eval方法,立即執行使用者輸入的程式碼時要十分小心。例如以下這段永遠不該出現的程式碼:
如果真的有需要執行使用者輸入的程式碼,一定要使用白名單驗證使用者輸入的資料。
SQL資料隱碼攻擊(SQL injections)
SQL資料隱碼攻擊指的是存取或改變未經授權機密資料,一般而言Rails開發者不必擔心這種攻擊,例如以下的程式碼,預設已經被移除惡意程式碼:
但是如果是下面的寫法是絕對應該要避免的:
駭客可能會用下面的連結取得所有使用者的資料:
要避免這一類的攻擊,可以使用?符號進行複雜查詢:
上面的例子使用者輸入的惡意資料已被移除。關於查詢還有更簡單的寫法,例如下面的例子:
可以改寫為:
表單劫奪(Form hijacking)
Rails5版本以後在網頁每個表單增加了單獨的token以避免受到跨網站請求攻擊。下面的例子是駭客修改了原來表單後的樣子:
HTML並不允許巢狀form標籤,因此表單送出之後表單的token將會傳送到駭客惡意的網站。如果從Rails5以前的版本移轉到新版,要確定已在config/initializers/new_framework_defaults.rb檔案內,將Rails.application.config.action_controller.per_form_csrf_tokens設為true。
揭露私密token(Revealing private tokens)
你的網站應用程式可能包含第三方API,小心這些token不應該被公開暴露,例如傳送到GitHub上。最簡單的處理方式是使用像dotenv-rails gem,將這些token存放在環境變數內。有了dotenv-rails你可以建立一個不受版控.env檔案,在.gitignore檔案加入:
然後在.env檔案存放你的token:
在正式環境下,這些變數通常存放在伺服器的config檔案,例如雲端伺服器Heroku使用下列指令:
在Rails4版本已在config/secrets.yml檔案內,將正式環境的token指定給ENV變數:
另外不要把secret_key_base的值設太簡單,可以使用rails secret產生較複雜的值。
記錄機密資料(Logging private data)
Rails有個不錯的功能,可以幫你記錄所有網頁互動資料,壞處是連機密資料也一併記錄。這樣一來任何接觸伺服器的人都有機會可以看到這些資料,例如密碼、信用卡等等。
在config/initializers/filter_parameter_logging.rb檔案內,預設的設定是這樣:
記得如果還有其他機密資料時,也要一併更新這個檔案。這個檔案裡面的符號將自動轉換為正則表達式(Regular Expression),所以任何欄位包含password在寫入log時將會被[FILTERED]取代。
透過HTTP傳送機密資料(Sending Sensitive Data via HTTP)
不要透過HTTP傳送未加密的敏感資料。例如有一個很簡單的身分驗證如下:
使用者名稱和密碼很容易透過類似Wireshark工具被駭客監聽,你應該在controller強制使用SSL:
force_ssl方法可以配合if和unless選項設定條件。也可以設定在config/application.rb檔案內,強迫整個網站應用程式都必須使用SSL:
上傳可執行檔案
許多網站允許使用者上傳自己的影像和文字檔,切記一定要檢查檔案大小並檢查檔案內容是否為可執行檔或腳本。
駭客可能傳送惡意檔案到你的伺服器並自動執行,例如駭客可能把檔案傳送到伺服器的公共目錄並將它設定為Apache的根目錄。
有一些常用解決方法在上傳時驗證檔案,例如Paperclip, Carrierwave, Dragonfly。
正則表達式(Regular Expressions)
通常Ruby的新手會把正則表達式的^和$當作字串的開頭和結尾。實際上應該是一列的頭和尾,不是整個字串的頭和尾。假設你有一個存放URL的欄位在資料表的website欄位裡面,有個view可以顯示使用者輸入的URL連結,看起來像這樣:
把要驗證使用者輸入的URL,寫成下面這個樣子並不安全:
駭客可能把URL寫成這個樣子:
如果這一串URL存放在資料庫的website欄位裡面,將會使得點選超連結的使用者執行駭客的惡意程式碼。要避免這種攻擊手法,可以把正則表達式的^和$改成\A和\z:
新版Rails為了避免此類錯誤,如果在格式驗證(validates :some_column, format: {...})使用正則表達式的^和$,將會觸發錯誤,如果執意要用,可以將選項multipart設為true。
另外一點,Rubular網站提供線上正則表達式編輯器。
透過IFrame內嵌網站
在HTML裡面可以透過iframe標籤把其他資源包含進來:
假設你有一個變更密碼網頁,包含2個欄位(變更後密碼和再次確認變更後密碼)和1個送出按鈕。駭客可能假造一個「贏取iphone」網頁,上面有2個欄位和1個送出按鈕,然後內嵌你的變更密碼網頁,但是把不透明度設為0,要求使用者在2個欄位輸入「I want iPhone!!」並按下送出按鈕,然後駭客告訴使用者你輸入的資料都是被隱藏看不到的。
結果使用者以為他是在「贏取iphone」網頁輸入資料,實際上是在你的網頁變更密碼,改變後的密碼是「I want iPhone!!」
通常這種攻擊手法不易成功,因為使用者看不到輸入的資料會起疑心,而且還要在沒有登出你的網站情況下,並保持session仍為連線狀態。
新版的Rails在檔案config/application.rb預設已經加入下列設定,以避免這類攻擊:
如此只有自己的網站下的網頁可以使用iframe,別人不行。
使用Brakeman進行資安問題掃描
Brakeman gem可以幫忙掃描網站是否存在常見嚴重的資安問題,在Gemfile加入下列幾行:
執行安裝:
然後可以用brakeman指令執行掃描,例如把掃描結果輸出成html檔案:
Brakeman將每個弱點分成高中低等級標籤,不過這個工具並不能找出所有潛在問題,不應只依賴輸出的報表。
結論
這篇文章還有許多沒涵蓋到的資安問題未討論,建議在正式環境上線前閱讀過官方安全指引,善用Brakeman幫助你找出可能的弱點。