防止CSRF的攻擊—Origin和Referer
為了防止CSRF的攻擊,我們建議修改浏覽器在發送POST請求的時候加上一個Origin字段,這個Origin字段主要是用來辨別出最初請求是從哪裡發起的。如果浏覽器不能确定源在哪裡,那麼在發送的請求裡面Origin字段的值就為空。
Origin字段的方式比Referer更人性化,因為它尊重了使用者的隐私。
(1)Origin字段裡隻包含是誰發起的請求,并沒有其他資訊 (通常情況下是方案,主機和活動文檔URL的端口)。跟Referer不一樣的是,Origin字段并沒有包含涉及到使用者隐私的URL路徑和請求内容,這個尤其重要。
(2)Origin字段隻存在于POST請求,而Referer則存在于所有類型的請求。
随便點選一個超連結(比如從搜尋清單裡或者企業intranet),并不會發送Origin字段,這樣可以防止敏感資訊的以外洩露。
在應對隐私問題方面,Origin字段的方法可能更能迎合使用者的口味。
用Origin字段的方法來防禦CSRF攻擊的時候,網站需要做到以下幾點:
(1)在所有能改變狀态的請求裡,包括登陸請求,都必須使用POST方法。對于一些特定的能改變狀态的GET請求必須要拒絕,這是為了對抗上文中提到過的論壇張貼的那種危害類型。
(2)對于那些有Origin字段但是值并不是我們希望的(包括值為空)請求,伺服器要一律拒絕。比如,伺服器可以拒絕一切Origin字段為外站的請求。
雖然Origin字段的設計非常簡單,但是用它來防禦CSRF攻擊可以起到很好的作用。
(1)去掉Origin字段。由于支援這種方法的浏覽器在每次POST請求的時候都會帶上源header,那麼網站就可以通過檢視是否存在這種Origin字段來确定請求是否是由支援這種方法的浏覽器發起的。這種設計能有效防止攻擊者将一個支援這種方法的浏覽器改變成不支援這種方法的浏覽器,因為即使你改變浏覽器去掉了Origin字段,Origin字段還是存在,隻不過值變為空了。這跟Referer很不一樣,因為Referer 隻要是在請求裡去掉了,那伺服器就探測不到了。
(2)DNS重新綁定。在現有的浏覽器裡面,對于同站的XMLHttpRequests,Origin字段可以被僞造。隻依賴網絡連接配接進行身份驗證的網站應當使用在第2章裡提到的DNS重新綁定的方法,比如驗證header裡的Host字段。在使用Origin字段來防禦CSRF攻擊的時候,也需要用到DNS重新綁定的方法,他們是相輔相成的。當然對于在第四章裡提到的CSRF防禦方法,也需要用到DNS重新綁定的方法。
(3)插件。如果網站根據crossdomain.xml準備接受一個跨站HTTP請求的時候,攻擊者可以在請求裡用Flash Player來設定Origin字段。在處理跨站請求的時候,token驗證的方法處理的不好,因為token會暴露。為了應對這些攻擊,網站不應當接受不可信來源的跨站請求。
(4)應用。Origin字段跟以下四個用來确定請求來源的建議非常類似。
Origin字段以下四個建議的基礎上統一并改進了,目前已經有幾個組織采用了Origin字段的方法建議。
(1) Cross-Site XMLHttp Request。Cross-Site XMLHttp Request的方法規定了一個Access-Control-Origin 字段,用來确定請求來源。這個字段存在于所有的HTTP方法,但是它隻在XMLHttpRequests請求的時候才會帶上。我們對Origin字段的設想就是來源于這個建議,而且Cross-Site XMLHttp Request工作組已經接受我們的建議願意将字段統一命名為Origin。
(2)XDomainRequest。在Internet Explorer 8 Beta 1裡有XDomainRequest的API,它在發送HTTP請求的時候将Referer裡的路徑和請求内容删掉了。被縮減後的Referer字段可以辨別請求的來源。我們的實驗結果表明這種删減的Referer字段經常會被拒絕,而我們的Origin字段卻不會。微軟已經發表聲明将會采用我們的建議将XDomainRequest裡的删減Referer更改為Origin字段。
(3)JSONRequest。在JSONRequest這種設計裡,包含有一個Domain字段用來辨別發起請求的主機名。相比之下,我們的Origin字段方法不僅包含有主機,還包含請求的方案和端口。JSONRequest規範的設計者已經接受我們的建議願意将Domain字段更改為Origin字段,以用來防止網絡攻擊。
(4)Cross-Document Messaging。在HTML5規範裡提出了一個建議,就是建立一個新的浏覽器API,用來驗證用戶端在HTML檔案之間連結。這種設計裡面包含一個不能被覆寫的origin屬性,如果不是在用戶端的話,在服務端驗證這種origin屬性的過程與我們驗證origin字段的過程其實是一樣的。
伺服器和浏覽器端都實作了利用origin字段的方法來防止CSRF攻擊。在浏覽器端我們的實作origin字段方式是,在WebKit添加一個8行代碼的更新檔,Safari裡有我們的開源元件,Firefox裡有一個466行代碼的插件。在伺服器端我們實作origin字段的方式是,在ModSecurity應用防火牆裡我們隻用3行代碼,在Apache裡添加一個應用防火牆語言(見圖4)。這些規則在POST請求裡能驗證Host字段和具有合法值的origin字段。在實作這些規則來防禦CSRF攻擊的時候,網站并不需要做出什麼改變,而且這些規則還能確定GET請求沒有任何攻擊性(前提是浏覽器端已經實作了origin字段方法)。