可以接著上一章來看:
三 Cross-Site Reference Forgery (CSRF)
- 這個攻擊方法包含惡意代碼或是一個用戶信任的已驗證的web應用頁面的鏈接。如果session沒有過期,攻擊者就可能執行未授權的命令 。
在session那一章裡,你已經了解,大多數的Rails應用都使用基於cookie的session。要麼他們在cookie裡存儲一個session id,服務端有 個session hash,要麼整個session hash都在客戶端。當向一個域名發送請求時,如果能找到這個域名的cookie,浏覽器會自動附帶上這個 cookie。但是,問題是,來自於不同域名的站點的請求,也會發送這個cookie。來看這個例子:
1.Bob同志浏覽了一個留言板和一個由hacker制作的html標簽內容。這個元素引用的是一個bob的項目管理應用程序裡的命令,而不是一個圖 片。
2.<img src="http://www.webapp.com/project/1/destroy">
3.Bob的session在www.webapp.com還是活的,因為他剛剛離開幾分鐘還沒有注銷。
4.當浏覽器發現這個img標簽,他試圖從www.webapp.com加載這個圖像,正如前面所說,他將會發送一個帶有有效session id的cookie(bob 的cookie, 他剛登陸www.webapp.com)。
5.位於www.webapp.com的web應用驗證了對應session hash的用戶信息並且刪除了id為一的那個project。之後它返回了一個出乎浏覽器意料 的結果,所以它沒有顯示圖像。
6.Bob並沒有注意到這次攻擊,但是一些天以後,他發現id為一的那個project離他而去了。
有重要的一點要注意,實際制作的圖像或鏈接不一定必須位於Web應用的網域,它可以在任何地方-在一個論壇,博客帖子或電子郵件。
CSRF是一個不可忽略的重要的安全問題。
3.1 CSRF對策
- 第一點,遵循W3C標准,正確使用GET和POST,第二點,在non-GET請求中使用一個安全token將使你遠離CSRF.
HTTP協議提供兩種類型的請求 - GET和POST(還有其他,但是大多數浏覽器不支持),萬維網聯盟( W3C )為HTTP GET或POST提供了一個選 擇清單:
Use GET if:
這個Interaction更像是問題,它是一個安全操作,比如,如查詢,讀操作,或查閱
Use POST if:
這個Interaction更像是命令,或者
這個Interaction依用戶期望的方式改變資源狀態,比如訂閱一個服務。
用戶為這個interaction產生的結果負責。
如果你的應用是restful的,你可能會用額外的http動詞,例如 PUT ,DELETE。 然而今天大多數的浏覽器並不支持它們,僅僅支持GET和 POST。Rails使用一個隱藏的_method來處理這一障礙(我在這篇文章http://blackanger.blog.51cto.com/140924/108678最後一段引用DHH的話 也說過)。
在一個controller裡的驗證方法確保具體的actions不會過度使用GET.
來看一個例子,僅在transfer 這個action裡使用post方法,如果使用了其他的HTTP動詞,則會被重定向到list 這個action:
verify :method => :post, :only => [:transfer], :redirect_to => {:action => :list}
這個防范措施,使攻擊失效,因為web應用不會接受浏覽器從images發送的一個get請求。但這僅僅是第一步,因為post請求也可以被自動發 送。下面的一個例子可以動態創建一個發送post請求的form表單:
<a href="http://www.harmless.com/" onclick="
var f = document.createElement ('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'POST';
f.action = 'http://www.example.com/account/destroy';
f.submit();
return false;">To the harmless survey</a>
或者,攻擊者可以把這些代碼放在圖片的鼠標滑過事件處理裡面:
<img src="http://www.harmless.com/img" width="400" height="400" onmouseover="…" />
這裡還有很多可能性,包括在後台對受害者進行ajax攻擊。解決的辦法是在服 務端對非GET請求使用security token, 在Rails2.x 版本,只需要在application controller裡加一行代碼:
protect_from_forgery :secret ⇒ "123456789012345678901234567890…"
它會在所有Rails裡生成的表 單和ajax請求裡自動包含一個根據當前的session和服務端的secret計算出來的security token。 你用CookieStorage作為session 存儲,就不 需要這個secret了。 如果這個security token和所期望的不匹配,就會拋出一個ActionController::InvalidAuthenticityToken 的異常。
請注意,跨站點腳本(xss)漏洞繞過所有的CSRF保護。XSS可以讓攻擊者訪問頁面的所有元素,所以他可以從一個表單裡讀取CSRF Security token, 或者直接提交表單,稍後會有更多xss的內容奉獻。