Files
build-web-application-with-…/zh-tw/09.1.md
2019-02-26 01:40:54 +08:00

5.6 KiB
Raw Blame History

9.1 預防CSRF攻擊

什麼是CSRF

CSRFCross-site request forgery中文名稱跨站請求偽造也被稱為one click attack/session riding縮寫為CSRF/XSRF。

那麼CSRF到底能夠幹嘛呢你可以這樣簡單的理解攻擊者可以盜用你的登陸資訊以你的身份模擬傳送各種請求。攻擊者只要藉助少許的社會工程學的詭計例如透過QQ等聊天軟體傳送的連結(有些還偽裝成短域名,使用者無法分辨)攻擊者就能迫使Web應用的使用者去執行攻擊者預設的操作。例如當用戶登入網路銀行去檢視其存款餘額在他沒有退出時就點選了一個QQ好友發來的連結那麼該使用者銀行帳戶中的資金就有可能被轉移到攻擊者指定的帳戶中。

所以遇到CSRF攻擊時將對終端使用者的資料和操作指令構成嚴重的威脅當受攻擊的終端使用者具有管理員帳戶的時候CSRF攻擊將危及整個Web應用程式。

CSRF的原理

下圖簡單闡述了CSRF攻擊的思想

圖9.1 CSRF的攻擊過程

從上圖可以看出要完成一次CSRF攻擊受害者必須依次完成兩個步驟

  • 1.登入受信任網站A並在本地產生Cookie 。
  • 2.在不退出A的情況下訪問危險網站B。

看到這裡讀者也許會問“如果我不滿足以上兩個條件中的任意一個就不會受到CSRF的攻擊”。是的確實如此但你不能保證以下情況不會發生

  • 你不能保證你登入了一個網站後不再開啟一個tab頁面並訪問另外的網站特別現在瀏覽器都是支援多tab的。
  • 你不能保證你關閉瀏覽器了後你本地的Cookie立刻過期你上次的會話已經結束。
  • 上圖中所謂的攻擊網站,可能是一個存在其他漏洞的可信任的經常被人訪問的網站。

因此對於使用者來說很難避免在登陸一個網站之後不點選一些連結進行其他操作所以隨時可能成為CSRF的受害者。

CSRF攻擊主要是因為Web的隱式身份驗證機制Web的身份驗證機制雖然可以保證一個請求是來自於某個使用者的瀏覽器但卻無法保證該請求是使用者批准傳送的。

如何預防CSRF

過上面的介紹,讀者是否覺得這種攻擊很恐怖,意識到恐怖是個好事情,這樣會促使你接著往下看如何改進和防止類似的漏洞出現。

CSRF的防禦可以從伺服器端和客戶端兩方面著手防禦效果是從伺服器端著手效果比較好現在一般的CSRF防禦也都在伺服器端進行。

伺服器端的預防CSRF攻擊的方式方法有多種但思想上都是差不多的主要從以下2個方面入手

  • 1、正確使用GET,POST和Cookie
  • 2、在非GET請求中增加偽隨機數

我們上一章介紹過REST方式的Web應用一般而言普通的Web應用都是以GET、POST為主還有一種請求是Cookie方式。我們一般都是按照如下方式設計應用

1、GET常用在檢視列舉展示等不需要改變資源屬性的時候

2、POST常用在下達訂單改變一個資源的屬性或者做其他一些事情

接下來我就以Go語言來舉例說明如何限制對資源的訪問方法


mux.Get("/user/:uid", getuser)
mux.Post("/user/:uid", modifyuser)

這樣處理後因為我們限定了修改只能使用POST當GET方式請求時就拒絕響應所以上面圖示中GET方式的CSRF攻擊就可以防止了但這樣就能全部解決問題了嗎當然不是因為POST也是可以模擬的。

因此我們需要實施第二步在非GET方式的請求中增加隨機數這個大概有三種方式來進行

  • 為每個使用者產生一個唯一的cookie token所有表單都包含同一個偽隨機值這種方案最簡單因為攻擊者不能獲得第三方的Cookie(理論上)所以表單中的資料也就構造失敗但是由於使用者的Cookie很容易由於網站的XSS漏洞而被盜取所以這個方案必須要在沒有XSS的情況下才安全。
  • 每個請求使用驗證碼,這個方案是完美的,因為要多次輸入驗證碼,所以使用者友好性很差,所以不適合實際運用。
  • 不同的表單包含一個不同的偽隨機值我們在4.4小節介紹“如何防止表單多次遞交”時介紹過此方案,複用相關程式碼,實現如下:

產生隨機數token


h := md5.New()
io.WriteString(h, strconv.FormatInt(crutime, 10))
io.WriteString(h, "ganraomaxxxxxxxxx")
token := fmt.Sprintf("%x", h.Sum(nil))

t, _ := template.ParseFiles("login.gtpl")
t.Execute(w, token)

輸出token


<input type="hidden" name="token" value="{{.}}">

驗證token


r.ParseForm()
token := r.Form.Get("token")
if token != "" {
	//驗證token的合法性
} else {
	//不存在token報錯
}

這樣基本就實現了安全的POST但是也許你會說如果破解了token的演算法呢按照理論上是但是實際上破解是基本不可能的因為有人曾計算過暴力破解該串大概需要2的11次方時間。

總結

跨站請求偽造即CSRF是一種非常危險的Web安全威脅它被Web安全界稱為“沉睡的巨人”其威脅程度由此“美譽”便可見一斑。本小節不僅對跨站請求偽造本身進行了簡單介紹還詳細說明造成這種漏洞的原因所在然後以此提了一些防範該攻擊的建議希望對讀者編寫安全的Web應用能夠有所啟發。