CSRF跨站請求僞造(Cross-Site Request Forgery),儘管聽起來像跨站腳本(XSS),但它與XSS很是不一樣,而且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則經過假裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊每每不大流行(所以對其進行防範的資源也至關稀少)和難以防範,因此被認爲比XSS更具危險性。javascript
能夠這樣來理解:攻擊者盜用了你的身份,以你的名義發送惡意請求,對服務器來講這個請求是徹底合法的,但卻完成了攻擊者所指望的一個操做,好比以你的名義發送郵件、發消息,盜取你的帳號,添加系統管理員,甚至於購買商品、虛擬貨幣轉帳等。html
CSRF漏洞通常分爲站外和站內兩種類型。java
CSRF站外類型的漏洞本質上就是傳統意義上的外部提交數據問題。一般程序員會考慮給一些留言或者評論的表單加上水印以防止SPAM問題(這裏,SPAM能夠簡單的理解爲垃圾留言、垃圾評論,或者是帶有站外連接的惡意回覆),可是有時爲了提升用戶的體驗性,可能沒有對一些操做作任何限制,因此攻擊者能夠事先預測並設置請求的參數,在站外的Web頁面裏編寫腳本僞造文件請求,或者和自動提交的表單一塊兒使用來實現GET、POST請求,當用戶在會話狀態下點擊連接訪問站外Web頁面,客戶端就被強迫發起請求。程序員
CSRF站內類型的漏洞在必定程度上是因爲程序員濫用$_REQUEST類變量形成的。在一些敏感的操做中(如修改密碼、添加用戶等),原本要求用戶從表單提交發起POST請求傳遞參數給程序,可是因爲使用了$_REQUEST等變量,程序除支持接收POST請求傳遞的參數外也支持接收GET請求傳遞的參數,這樣就會爲攻擊者使用CSRF攻擊創造條件。通常攻擊者只要把預測的請求參數放在站內一個貼子或者留言的圖片連接裏,受害者瀏覽了這樣的頁面就會被強迫發起這些請求。web
其中Web A爲存在CSRF漏洞的網站,Web B爲攻擊者構建的惡意網站,User C爲Web A網站的合法用戶。算法
1. 用戶C打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼請求登陸網站A; 2.在用戶信息經過驗證後,網站A產生Cookie信息並返回給瀏覽器,此時用戶登陸網站A成功,能夠正常發送請求到網站A; 3. 用戶未退出網站A以前,在同一瀏覽器中,打開一個TAB頁訪問網站B; 4. 網站B接收到用戶請求後,返回一些攻擊性代碼,併發出一個請求要求訪問網站A; 5. 瀏覽器在接收到這些攻擊性代碼後,根據網站B的請求,在用戶不知情的狀況下攜帶Cookie信息,向網站A發出請求。網站A並不知道該請求實際上是由B發起的,因此會根據用戶C的Cookie信息以C的權限處理該請求,致使來自網站B的惡意代碼被執行。
從上圖能夠看出,要完成一次CSRF攻擊,受害者必須依次完成兩個步驟:瀏覽器
看到這裏,你也許會說:「若是我不知足以上兩個條件中的一個,我就不會受到CSRF的攻擊」。是的,確實如此,但你不能保證如下狀況不會發生:安全
CSRF攻擊是源於WEB的隱式身份驗證機制!WEB的身份驗證機制雖然能夠保證一個請求是來自於某個用戶的瀏覽器,但卻沒法保證該請求是用戶批准發送的!服務器
CSRF 攻擊能夠在受害者絕不知情的狀況下以受害者名義僞造請求發送給受攻擊站點,從而在並未受權的狀況下執行在權限保護之下的操做。cookie
好比:
受害者 Bob 在銀行有一筆存款,經過對銀行的網站發送請求
http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可使Bob 把 1000000 的存款轉到 bob2 的帳號下。一般狀況下,該請求發送到網站後,服務器會先驗證該請求是否來自一個合法的 session,而且該 session 的用戶 Bob 已經成功登錄。
黑客 Mallory 本身在該銀行也有帳戶,他知道上文中的URL能夠把錢進行轉賬操做。Mallory能夠本身發送一個請求給銀行:
http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。可是這個請求來自 Mallory 而非 Bob,他不能經過安全認證,所以該請求不會起做用。
這時,Mallory 想到使用 CSRF 的攻擊方式,他先本身作一個網站,在網站中放入以下代碼:
src=」http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory 」,而且經過廣告等誘使 Bob 來訪問他的網站。當Bob訪問該網站時,上述 url 就會從 Bob 的瀏覽器發向銀行,而這個請求會附帶 Bob 瀏覽器中的 cookie 一塊兒發向銀行服務器。大多數狀況下,該請求會失敗,由於他要求 Bob 的認證信息。可是,若是Bob當時恰巧剛訪問他的銀行後不久,他的瀏覽器與銀行網站之間的session還沒有過時,瀏覽器的cookie之中含有 Bob的認證信息。這時,悲劇發生了,這個url請求就會獲得響應,錢將從Bob的帳號轉移到Mallory的帳號,而 Bob 當時絕不知情。等之後 Bob 發現帳戶錢少了,即便他去銀行查詢日誌,他也只能發現確實有一個來自於他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而Mallory則能夠拿到錢後逍遙法外。
在討論如何抵禦 CSRF 以前,先要明確 CSRF 攻擊的對象,也就是要保護的對象。從以上的例子可知,CSRF 攻擊是黑客藉助受害者的 cookie 騙取服務器的信任,可是黑客並不能拿到 cookie,也看不到 cookie 的內容。另外,對於服務器返回的結果,因爲瀏覽器同源策略的限制,黑客也沒法進行解析。所以,黑客沒法從返回的結果中獲得任何東西,他所能作的就是給服務器發送請求,以執行請求中所描述的命令,在服務器端直接改變數據的值,而非竊取服務器中的數據。因此,咱們要保護的對象是那些能夠直接產生數據改變的服務,而對於讀取數據的服務,則不須要進行 CSRF 的保護。好比銀行系統中轉帳的請求會直接改變帳戶的金額,會遭到 CSRF 攻擊,須要保護。而查詢餘額是對金額的讀取操做,不會改變數據,CSRF 攻擊沒法解析服務器返回的結果,無需保護。
檢測CSRF漏洞是一項比較繁瑣的工做,最簡單的方法就是抓取一個正常請求的數據包,去掉Referer字段後再從新提交,若是該提交還有效,那麼基本上能夠肯定存在CSRF漏洞。
隨着對CSRF漏洞研究的不斷深刻,不斷涌現出一些專門針對CSRF漏洞進行檢測的工具,如CSRFTester,CSRF Request Builder等。
以CSRFTester工具爲例,CSRF漏洞檢測工具的測試原理以下:使用CSRFTester進行測試時,首先須要抓取咱們在瀏覽器中訪問過的全部連接以及全部的表單等信息,而後經過在CSRFTester中修改相應的表單等信息,從新提交,這至關於一次僞造客戶端請求。若是修改後的測試請求成功被網站服務器接受,則說明存在CSRF漏洞,固然此款工具也能夠被用來進行CSRF攻擊。
CSRF漏洞防護主要能夠從三個層面進行,即服務端的防護、用戶端的防護和安全設備的防護。
根據 HTTP 協議,在 HTTP 頭中有一個字段叫 Referer,它記錄了該 HTTP請求的來源地址。在一般狀況下,訪問一個安全受限頁面的請求來自於同一個網站,好比須要訪問 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用戶必須先登錄 bank.example,而後經過點擊頁面上的按鈕來觸發轉帳事件。這時,該轉賬請求的 Referer 值就會是轉帳按鈕所在的頁面的 URL,一般是以 bank.example 域名開頭的地址。而若是黑客要對銀行網站實施 CSRF 攻擊,他只能在他本身的網站構造請求,當用戶經過黑客的網站發送請求到銀行時,該請求的 Referer 是指向黑客本身的網站。所以,要防護 CSRF 攻擊,銀行網站只須要對於每個轉帳請求驗證其 Referer 值,若是是以 bank.example 開頭的域名,則說明該請求是來自銀行網站本身的請求是合法的。若是 Referer 是其餘網站的話,則有多是黑客的CSRF攻擊,拒絕該請求。
這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不須要操心 CSRF 的漏洞,只須要在最後給全部安全敏感的請求統一增長一個攔截器來檢查 Referer 的值就能夠。特別是對於當前現有的系統,不須要改變當前系統的任何已有代碼和邏輯,沒有風險,很是便捷。
然而,這種方法並不是萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,可是每一個瀏覽器對於 Referer 的具體實現可能有差異,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來說,這樣並不安全。事實上,對於某些瀏覽器,好比 IE6 或 FF2,目前已經有一些方法能夠篡改 Referer 值。若是 bank.example 網站支持 IE6 瀏覽器,黑客徹底能夠把用戶瀏覽器的 Referer 值設爲以 bank.example 域名開頭的地址,這樣就能夠經過驗證,從而進行 CSRF 攻擊。
即使是使用最新的瀏覽器,黑客沒法篡改 Referer 值,這種方法仍然有問題。由於 Referer 值會記錄下用戶的訪問來源,有些用戶認爲這樣會侵犯到他們本身的隱私權,特別是有些組織擔憂 Referer 值會把組織內網中的某些信息泄露到外網中。所以,用戶本身能夠設置瀏覽器使其在發送請求時再也不提供 Referer。當他們正常訪問銀行網站時,網站會由於請求沒有 Referer 值而認爲是 CSRF 攻擊,拒絕合法用戶的訪問。
CSRF 攻擊之因此可以成功,是由於黑客能夠徹底僞造用戶的請求,該請求中全部的用戶驗證信息都是存在於 cookie 中,所以黑客能夠在不知道這些驗證信息的狀況下直接利用用戶本身的 cookie 來經過安全驗證。要抵禦 CSRF,關鍵在於在請求中放入黑客所不能僞造的信息,而且該信息不存在於 cookie 之中。能夠在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端創建一個攔截器來驗證這個 token,若是請求中沒有 token 或者 token 內容不正確,則認爲多是 CSRF 攻擊而拒絕該請求。
這種方法要比檢查 Referer 要安全一些,token 能夠在用戶登錄後產生並放於 session 之中,而後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。對於 GET 請求,token 將附在請求地址以後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來講,要在 form 的最後加上 <input type=」hidden」 name=」csrftoken」 value=」tokenvalue」/>,這樣就把 token 以參數的形式加入請求了。可是,在一個網站中,能夠接受請求的地方很是多,要對於每個請求都加上 token 是很麻煩的,而且很容易漏掉,一般使用的方法就是在每次頁面加載時,使用 javascript 遍歷整個 dom 樹,對於 dom 中全部的 a 和 form 標籤後加入 token。這樣能夠解決大部分的請求,可是對於在頁面加載以後動態生成的 html 代碼,這種方法就沒有做用,還須要程序員在編碼時手動添加 token。
該方法還有一個缺點是難以保證 token 自己的安全。特別是在一些論壇之類支持用戶本身發表內容的網站,黑客能夠在上面發佈本身我的網站的地址。因爲系統也會在這個地址後面加上 token,黑客能夠在本身的網站上獲得這個 token,並立刻就能夠發動 CSRF 攻擊。爲了不這一點,系統能夠在添加 token 的時候增長一個判斷,若是這個連接是鏈到本身本站的,就在後面添加 token,若是是通向外網則不加。不過,即便這個 csrftoken 不以參數的形式附加在請求之中,黑客的網站也一樣能夠經過 Referer 來獲得這個 token 值以發動 CSRF 攻擊。這也是一些用戶喜歡手動關閉瀏覽器 Referer 功能的緣由。
這種方法也是使用 token 並進行驗證,和上一種方法不一樣的是,這裏並非把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裏。經過 XMLHttpRequest 這個類,能夠一次性給全部該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,經過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔憂 token 會透過 Referer 泄露到其餘網站中去。
然而這種方法的侷限性很是大。XMLHttpRequest 請求一般用於 Ajax 方法中對於頁面局部的異步刷新,並不是全部的請求都適合用這個類來發起,並且經過該類請求獲得的頁面不能被瀏覽器所記錄下,從而進行前進,後退,刷新,收藏等操做,給用戶帶來不便。另外,對於沒有進行 CSRF 防禦的遺留系統來講,要採用這種方法來進行防禦,要把全部請求都改成 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。
這個方案的思路是:每次的用戶提交都須要用戶在表單中填寫一個圖片上的隨機字符串,厄….這個方案能夠徹底解決CSRF,但我的以爲在易用性方面彷佛不是太好,還有聽聞是驗證碼圖片的使用涉及了一個被稱爲MHTML的Bug,可能在某些版本的微軟IE中受影響。
下文將以 Java 爲例,對上述三種方法分別用代碼進行示例。不管使用何種方法,在服務器端的攔截器必不可少,它將負責檢查到來的請求是否符合要求,而後視結果而決定是否繼續請求或者丟棄。在 Java 中,攔截器是由 Filter 來實現的。咱們能夠編寫一個 Filter,並在 web.xml 中對其進行配置,使其對於訪問全部須要 CSRF 保護的資源的請求進行攔截。
在 filter 中對請求的 Referer 驗證代碼以下
// 從 HTTP 頭中取得 Referer 值 String referer=request.getHeader("Referer"); // 判斷 Referer 是否以 bank.example 開頭 if((referer!=null) &&(referer.trim().startsWith(「bank.example」))){ chain.doFilter(request, response); }else{ request.getRequestDispatcher(「error.jsp」).forward(request,response); }
chain.doFilter()函數是過濾器。過濾器顧名思義就是在執行某件事情的時候開始以前。開始進行處理的叫作過濾處理。一個方法,一個類就是充當過濾器的角色。它是在一個容器(相似於Tomcat)啓動以後,打開一網站,他就會根據配置就行過濾處理。doFilter()方法就是過濾器的主要方法,init()方法就是在容器啓動的時候執行的。 doFilter(request,response)的方法就是在你打開一個頁面當知足過濾器的條件的時候,他就會繼續執行你打開頁面時候的操做。同時若是你使用了httpservletrequest和httpservletResponse的話,獲取servletPath的值就是你要打開的文件的地址。
以上代碼先取得 Referer 值,而後進行判斷,當其非空並以 bank.example 開頭時,則繼續請求,不然的話多是 CSRF 攻擊,轉到 error.jsp 頁面。
若是要進一步驗證請求中的 token 值,代碼以下
HttpServletRequest req = (HttpServletRequest)request; HttpSession s = req.getSession(); // 從 session 中獲得 csrftoken 屬性 String sToken = (String)s.getAttribute(「csrftoken」); if(sToken == null){ // 產生新的 token 放入 session 中 sToken = generateToken(); s.setAttribute(「csrftoken」,sToken); chain.doFilter(request, response); } else{ // 從 HTTP 頭中取得 csrftoken String xhrToken = req.getHeader(「csrftoken」); // 從請求參數中取得 csrftoken String pToken = req.getParameter(「csrftoken」); if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ chain.doFilter(request, response); }else if(sToken != null && pToken != null && sToken.equals(pToken)){ chain.doFilter(request, response); }else{ request.getRequestDispatcher(「error.jsp」).forward(request,response); } }
首先判斷 session 中有沒有 csrftoken,若是沒有,則認爲是第一次訪問,session 是新創建的,這時生成一個新的 token,放於 session 之中,並繼續執行請求。若是 session 中已經有 csrftoken,則說明用戶已經與服務器之間創建了一個活躍的 session,這時要看這個請求中有沒有同時附帶這個 token,因爲請求可能來自於常規的訪問或是 XMLHttpRequest 異步訪問,咱們分別嘗試從請求中獲取 csrftoken 參數以及從 HTTP 頭中獲取 csrftoken 自定義屬性並與 session 中的值進行比較,只要有一個地方帶有有效 token,就斷定請求合法,能夠繼續執行,不然就轉到錯誤頁面。生成 token 有不少種方法,任何的隨機算法均可以使用,Java 的 UUID 類也是一個不錯的選擇。
除了在服務器端利用 filter 來驗證 token 的值之外,咱們還須要在客戶端給每一個請求附加上這個 token,這是利用 js 來給 html 中的連接和表單請求地址附加 csrftoken 代碼,其中已定義 token 爲全局變量,其值能夠從 session 中獲得。
function appendToken(){ updateForms(); updateTags(); } function updateForms() { // 獲得頁面中全部的 form 元素 var forms = document.getElementsByTagName('form'); for(i=0; i<forms.length; i++) { var url = forms[i].action; // 若是這個 form 的 action 值爲空,則不附加 csrftoken if(url == null || url == "" ) continue; // 動態生成 input 元素,加入到 form 以後 var e = document.createElement("input"); e.name = "csrftoken"; e.value = token; e.type="hidden"; forms[i].appendChild(e); } } function updateTags() { var all = document.getElementsByTagName('a'); var len = all.length; // 遍歷全部 a 元素 for(var i=0; i<len; i++) { var e = all[i]; updateTag(e, 'href', token); } } function updateTag(element, attr, token) { var location = element.getAttribute(attr); if(location != null && location != '' '' ) { var fragmentIndex = location.indexOf('#'); var fragment = null; if(fragmentIndex != -1){ //url 中含有隻至關頁的錨標記 fragment = location.substring(fragmentIndex); location = location.substring(0,fragmentIndex); } var index = location.indexOf('?'); if(index != -1) { //url 中已含有其餘參數 location = location + '&csrftoken=' + token; } else { //url 中沒有其餘參數 location = location + '?csrftoken=' + token; } if(fragment != null){ location += fragment; } element.setAttribute(attr, location); } }
在客戶端 html 中,主要是有兩個地方須要加上 token,一個是表單 form,另外一個就是連接 a。這段代碼首先遍歷全部的 form,在 form 最後添加一隱藏字段,把 csrftoken 放入其中。而後,代碼遍歷全部的連接標記 a,在其 href 屬性中加入 csrftoken 參數。注意對於 a.href 來講,可能該屬性已經有參數,或者有錨標記。所以須要分狀況討論,以不一樣的格式把 csrftoken 加入其中。
若是你的網站使用 XMLHttpRequest,那麼還須要在 HTTP 頭中自定義 csrftoken 屬性,利用 dojo.xhr 給 XMLHttpRequest 加上自定義屬性代碼以下:
var plainXhr = dojo.xhr; // 重寫 dojo.xhr 方法 dojo.xhr = function(method,args,hasBody) { // 確保 header 對象存在 args.headers = args.header || {}; tokenValue = '<%=request.getSession(false).getAttribute("csrftoken")%>'; var token = dojo.getObject("tokenValue"); // 把 csrftoken 屬性放到頭中 args.headers["csrftoken"] = (token) ? token : " "; return plainXhr(method,args,hasBody); };
這裏改寫了 dojo.xhr 的方法,首先確保 dojo.xhr 中存在 HTTP 頭,而後在 args.headers 中添加 csrftoken 字段,並把 token 值從 session 裏拿出放入字段中。
對於普通用戶來講,都學習並具有網絡安全知識以防護網絡攻擊是不現實的。但若用戶養成良好的上網習慣,則可以很大程度上減小CSRF攻擊的危害。例如,用戶上網時,不要輕易點擊網絡論壇、聊天室、即時通信工具或電子郵件中出現的連接或者圖片;及時退出長時間不使用的已登陸帳戶,尤爲是系統管理員,應儘可能在登出系統的狀況下點擊未知連接和圖片。除此以外,用戶還須要在鏈接互聯網的計算機上安裝合適的安全防禦軟件,並及時更新軟件廠商發佈的特徵庫,以保持安全軟件對最新攻擊的實時跟蹤。
因爲從漏洞的發現到補丁的發佈須要必定的時間,並且至關比例的廠商對漏洞反應不積極,再加之部分系統管理員對系統補丁的不夠重視,這些都給了攻擊者可乘之機。鑑於上述各類狀況,用戶能夠藉助第三方的專業安全設備增強對CSRF漏洞的防護。
CSRF攻擊的本質是攻擊者僞造了合法的身份,對系統進行訪問。若是可以識別出訪問者的僞造身份,也就能識別CSRF攻擊。研究發現,有些廠商的安全產品能基於硬件層面對HTTP頭部的Referer字段內容進行檢查來快速準確的識別CSRF攻擊。圖3展現了這種防護方式的簡圖。目前H3C公司的IPS產品採用了特殊技術,支持對部分經常使用系統的CSRF漏洞攻擊進行檢測和阻斷。
安全設備的傳統防護方式
經過上文討論可知,目前業界應對 CSRF 攻擊有一些剋制方法,可是每種方法都有利弊,沒有一種方法是完美的。如何選擇合適的方法很是重要。若是網站是一個現有系統,想要在最短期內得到必定程度的 CSRF 的保護,那麼驗證 Referer 的方法是最方便的,要想增長安全性的話,能夠選擇不支持低版本瀏覽器,畢竟就目前來講,IE7+, FF3+ 這類高版本瀏覽器的 Referer 值還沒法被篡改。
若是系統必須支持 IE6,而且仍然須要高安全性。那麼就要使用 token 來進行驗證,在大部分狀況下,使用 XmlHttpRequest 並不合適,token 只能以參數的形式放於請求之中,若你的系統不支持用戶本身發佈信息,那這種程度的防禦已經足夠,不然的話,你仍然難以防範 token 被黑客竊取並發動攻擊。在這種狀況下,你須要當心規劃你網站提供的各類服務,從中間找出那些容許用戶本身發佈信息的部分,把它們與其餘服務分開,使用不一樣的 token 進行保護,這樣能夠有效抵禦黑客對於你關鍵服務的攻擊,把危害降到最低。畢竟,刪除別人一個帖子比直接從別人帳號中轉走大筆存款嚴重程度要輕的多。
若是是開發一個全新的系統,則抵禦 CSRF 的選擇要大得多。筆者建議對於重要的服務,能夠儘可能使用 XMLHttpRequest 來訪問,這樣增長 token 要容易不少。另外儘可能避免在 js 代碼中使用複雜邏輯來構造常規的同步請求來訪問須要 CSRF 保護的資源,好比 window.location 和 document.createElement(「a」) 之類,這樣也能夠減小在附加 token 時產生的沒必要要的麻煩。
最後,要記住 CSRF 不是黑客惟一的攻擊手段,不管你 CSRF 防範有多麼嚴密,若是你係統有其餘安全漏洞,好比跨站域腳本攻擊 XSS,那麼黑客就能夠繞過你的安全防禦,展開包括 CSRF 在內的各類攻擊,你的防線將如同虛設。
可見,CSRF 是一種危害很是大的攻擊,又很難以防範。目前幾種防護策略雖然能夠很大程度上抵禦 CSRF 的攻擊,但並無一種完美的解決方案。一些新的方案正在研究之中,好比對於每次請求都使用不一樣的動態口令,把 Referer 和 token 方案結合起來,甚至嘗試修改 HTTP 規範,可是這些新的方案尚不成熟,要正式投入使用並被業界廣爲接受還需時日。在這以前,咱們只有充分重視 CSRF,根據系統的實際狀況選擇最合適的策略,這樣才能把 CSRF 的危害降到最低。
http://netsecurity.51cto.com/art/201609/518323.htm
https://blog.csdn.net/stpeace/article/details/53512283
https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/
http://www.h3c.com/cn/d_201208/751467_30008_0.htm
http://blog.nsfocus.net/fix-summary-csrf/