天天看點

Rails每周一題(六): Security Guide(上)

此篇文章總結自:http://guides.rubyonrails.org/security.html

謝謝某同學的提醒。

Web應用存在的安全問題包括賬号劫持,繞過通路控制,讀取或者修改敏感資訊或者顯示欺詐内容等。通過security guide系列篇讓我們一起來看看應該如何正确使用Rails來克服這些問題。

在上篇中,主要描述對session的攻擊,以及應對方法。

首先簡單介紹一下session的基本概念和一些普遍攻擊方法。

什麼是Session

Session是一個儲存特定使用者資訊的哈希,用一個session id來識别不同的session。從伺服器端發送到用戶端浏覽器的每一個響應中,cookie都包含一個session id。

在Rails裡面通過此種方式儲存和擷取session哈希内的值:

session[:user_id] = @current_user.id 
User.find(session[:user_id]) 
           

一個session id由一個随機字元串的哈希值組。這個随機字元串由目前時間,0和1之間的随機數,Ruby解釋器的程序id和一個字元串常量組成。是以基本上不可能暴力破解session id。

Session劫持

— 通過盜取使用者的session id,可以讓攻擊者以被盜取使用者的名義使用web應用。

現在大多數網站所使用的認證方式是form-based認證方式 。是以,隻要黑客盜取了使用者的session id,它既可以以被盜取使用者的名義使用web應用。

盜取session id的方式有以下幾種:

  • 監聽非安全網絡盜取cookie,比如無線網絡。可以通過提供基于SSL的安全連接配接來避免這個問題。
  • 很多人在公共場合使用了網站之後,忘記登出。是以網站要提供明顯的logout功能。
  • 通過跨站點腳本(XSS) 攻擊擷取使用者的cookie。
  • 定置使用者的session id。

Session使用準則

— 幾條關于使用cookie的準則。

     1. 不要在session裡面存儲大量資訊。正确的方式是在資料庫裡存儲對象資訊,而在session裡隻存儲id。這不僅會緩解session同步問題,而且在對象結構改變時不需要擔心遺留在session裡面的資料結構不一緻問題。

     2. 不要在session裡面存儲重要資料,以免丢失。特别是當你使用用戶端儲存session時,要當心被盜問題。(用戶端儲存session,指的是session在伺服器端和用戶端之間傳輸整個session,而不是session id。)

Session存儲機制

— Rails提供了多種session存儲機制,最重要的是ActiveRecordStore和CookieStore。

因為性能和可維護性,大多數人選擇ActiveRecordStore而不是檔案存儲。

而CookieStore由于在伺服器和用戶端之間傳輸整個session,雖然此種方式緩解了伺服器的壓力,但卻有很多限制:

1. Cookie有嚴格的大小控制 -- 4K。

2. 因為session資訊在用戶端以明文形式存儲(Base64編碼),是以有很大的安全隐患。當然,我們可以通過加密方式來防止資訊的盜看。方法很簡單,在environment.rb裡面加上此句:

config.action_controller.session = {  :key => '_app_session',  :secret => '0x0dkfj3927dkc7djdh36rkckdfzsg...' } 
           

要注意的是需使用一個複雜的secret。

介紹完session的一些基本概念之後,來讓我們來看看幾種對session的攻擊方法和應對政策。

CookieStore Session的重播攻擊

假設我們使用CookieStore來存儲session,将會帶來這樣的安全問題:

1. 使用者擷取信用卡資訊,餘額存儲于session(很差的主意,示範之用。)

2. 使用者購買了一些東西。

3. 現在在session裡面存儲的餘額是購買之後扣去購買費用之後的餘額。

4. 使用者用第一步中的session替換現在的session。

5. 使用者的餘額被恢複到初始時。

我們可以通過nonce 解決這個問題,但nonce需要伺服器端資料庫的跟蹤,是以這已經違背了使用CookieStore避免使用資料庫的初衷。

是以,問題的關鍵所在還是在于不應在session裡面存儲重要資訊。

Session定置

— 攻擊者通過定置其他使用者的session id,而非盜取session id的方式來使用其它使用者的賬戶。

Rails每周一題(六): Security Guide(上)

讓我們看看這種攻擊是如何發生的:

  1. 攻擊者通路網站,并從cookie中擷取session。 (見圖中第1 和 第2步).
  2. 攻擊者通過不斷通路網站使session保持不過期。
  3. 攻擊者強迫受害者使用他的session id(見圖中第3步)。由于不可能更改另一個域的cookie(由于同一來源政策 -- same origin policy ), 攻擊者通過在被攻擊網站上運作一段如下的JavaScript代碼來更改受害者的cookie。JavaScript代碼的注入通過XSS的協助完成。
    <script>
document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
</script>.      
  4. 受害者在浏覽被注入惡意代碼的網頁時被更改了cookie值。
  5. 伺服器發現此session id并未登入,就要求受害者重新登入。
  6. 從此刻開始,攻擊者和受害者使用的是通過一個session id。而受害者對此一無所知。

應對政策:

隻需要一行代碼:

reset_session 
           

reset_session會清空舊session裡的所有值,并且建立一個新session。在使用者登入成功後運作此句,會讓session定置攻擊失效。不過要注意的是,需要轉移舊session中的值到新session。如果你使用的是RestfulAuthentication插件,那麼在SessionsController#create 方法中加入此句。

還有一種應對方法是在session裡面加上一些使用者特定的資訊,比如IP位址等。但用此種方法得注意IP位址并不一定直接對應到确定的使用者,很多使用者在proxy後面通路。

Session過期問題

讓session在很長的時間内都不過期,會增大安全隐患。

讓session過期有兩種方式:

1. 在用戶端:在session id cookie上設定一個過期時間。但使用者可以通過修改cookie來改變過期時限。

2. 在服務端,我們可以通過如下代碼來清除未使用時間超過20分鐘的session:

class Session < ActiveRecord::Base 







  def self.sweep(time_ago = nil) 
 







     time = case time_ago 
 







          when /^(\d+)m$/ then Time.now - $1.to_i.minute 
 







          when /^(\d+)h$/ then Time.now - $1.to_i.hour 
 







          when /^(\d+)d$/ then Time.now - $1.to_i.day 
 







          else Time.now - 1.hour 
 







     end 
 







     self.delete_all "updated_at < '#{time.to_s(:db)}'" 
 







  end 








end 
           

當然,攻擊者可以通過每隔幾分鐘通路一次網站來保持session,可以加上這一段代碼來清除建立時間過久的session:

self.delete_all "updated_at < '#{time.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'" 
           

未完待續,請看Security Guide中篇 和下篇 。

繼續閱讀