當前位置:
首頁 > 知識 > 淺談csrf攻擊以及yii2對其的防範措施

淺談csrf攻擊以及yii2對其的防範措施

淺談csrf攻擊以及yii2對其的防範措施

歡迎關注開源中國頭條號獲取更多優質文章


凡是我yii2學習社群的成員都知道,我不止一次給大家說構造表單100%使用yii2的ActiveForm來實現,這除了能和AR更好結合外就是自動生成csrf隱藏域,一個非常安全的舉措。

今天北哥就給大家普及下csrf是啥?如果你已經知道了可以直接拉文章到底部點個贊。:smile:

CSRF(Cross-site request forgery跨站請求偽造)是一種對網站的惡意利用,在 2007 年曾被列為互聯網 20 大安全隱患之一。

關於CSRF,要從一個故事開始~

老王丟錢事件

這個故事要從程序員老王丟了1萬塊錢說起,總之是進了小偷,找回無果。丟錢後的老王一直在思考,錢是怎麼丟的、為何丟錢、為何是我丟錢~~

後來老王出現了嚴重的心理問題,他決定報復社會。

老王首先研究了網銀系統,他發現轉賬是通過GET形式

https://bank.abc.com/withdraw?account=liuxiaoer&amount=1000&to=abei

這意思就是說將 liuxiaoer 的1000元錢轉給abei,當然當請求到達銀行伺服器後,程序會驗證該請求是否來自合法的session並且該session的用戶就是 liuxiaoer 並且已經登錄。

老王自己也有一個銀行賬號 wang2,他嘗試登錄並且通過瀏覽器發送請求給銀行,代碼如下

https://bank.abc.com/withdraw?account=liuxiaoer&amount=1000&to=wang2

失敗了~因為當前登錄賬號是老王自己,發送請求後伺服器發現session所屬用戶wang2和account=liuxiaoer並不是一個人,因此被拒絕。

也就是說這個操作必須是 liuxiaoer 自己才可以,復仇的力量是可怕的,老王通過facebook層層找到了 liuxiaoer 就是快遞員老劉的銀行賬號。

於是一個偉大的計劃誕生了,老王的計劃是這樣的。

1、首先做一個網頁,在網頁中加入如下代碼

src="https://bank.abc.com/withdraw?account=liuxiaoer&amount=1000&to=wang2"

然後通過各種情景讓老劉(liuxiaoer)訪問了此網頁。

2、當老劉(liuxiaoer)訪問此網頁後,上面的請求會被發送到銀行,此刻還會帶著老劉(liuxiaoer)自己的瀏覽器cookie信息,當然這樣一般也不會成功,因為銀行伺服器發現老劉(liuxiaoer)並不在登錄狀態。

3、老王想了一個招,他在淘寶找了一個灰色商人老李,讓他通過種種方法,總之讓老劉(liuxiaoer)通過瀏覽器給老李轉了一次款。

4、就在第三步操作的2分鐘內,老王成功讓老劉(liuxiaoer)再一次訪問了自己做的網頁,你知道的,此刻老劉(liuxiaoer)在銀行的session還沒有過期,老王網頁給銀行伺服器發送請求後,驗證通過,打款成功。

5、老王收到了款,老劉(liuxiaoer)並不知道這一切,對於銀行來說這是一筆在正常不過的轉賬。

這就是CSRF攻擊,瀏覽器無法攔截。

CSRF攻擊特點

基於上面血淋淋的故事,我們總結下CSRF攻擊的幾個特點。

  • 黑客藉助於受害者的cookie等瀏覽器信息騙取伺服器新人,黑客並拿不到cookie等。
  • 由於瀏覽器同源策略,黑客無法拿到攻擊的響應結果,能做的只是發起請求,你是否還記得很多釣魚網站都模擬了登錄框么?
  • CSRF攻擊主要是發送修改數據請求。

CSRF防禦對象

因此我們要保護的是所有能引起數據變化的客戶端請求,比如新建、更新和刪除。

CSRF防禦方案

基於CSRF攻擊特點,在業界目前防禦 CSRF 攻擊主要有三種策略:

  • 驗證 HTTP Referer 欄位;
  • 在請求地址中添加 token 並驗證;
  • 在 HTTP 頭中自定義屬性並驗證。

HEEP Referer

在http請求的時候,頭部有一個叫做Referer的欄位,該欄位記錄本次請求的來源地址。因此伺服器端可以通過此欄位是否為同一個域名來判斷請求是否合法,因為客戶自己做的網頁發起的請求,其Referer為黑客網站。

這種方法最簡單,並且不需要修改業務代碼,我們只需要對到達伺服器的每個請求做一次攔截分析即可。

但是此方法的缺點也是明顯的,因為Referer的值是瀏覽器的,雖然HTTP協議不允許去修改,但是如果瀏覽器自身存在漏洞,那麼就有可能導致Referer被人工設置,不安全。

比如IE6,就可以通過方法篡改Referer值。

就算是最新的瀏覽器此方法也不是絕對可用的,這涉及了用戶的隱私,很多用戶會設置瀏覽器不提供Referer,因此伺服器在得不到Referer的情況下不能貿然的決絕服務,有可能這是一個合法請求。

添加Token

CSRF攻擊之所以能成功,是因為黑客完全偽造了一次用戶的正常請求(這也是瀏覽器無法攔截的原因),並且cookie信息就是用戶自己的,那麼我們如果在請求中放入一些黑客無法去偽造的信息(不存在與cookie中),不就可以抵禦了么!

比如在請求前生成一個token放到session中,當請求發生時,將token從session拿出來和請求提交過來的token進行對比,如果相等則認證通過,否則拒絕。token時刻在變化,黑客無法去偽造。

針對於不同類型的請求一般方案是

  • GET 放到url中,比如http://url?csrftoken=xxxx
  • POST 放到表單的隱藏域 <input type=」hidden」 name=」csrftoken」 value=」xxxx」/>

對於GET請求,這裡有一點要說明,在一個網站中請求的url很多,一般情況我們是通過js對dom的所有節點進行遍歷,發現a鏈接就在其href中增加token。

這裡存在一個問題,比如黑客將自己網站的鏈接發到了要攻擊頁面,則黑客網站鏈接後面會有一個token,此刻客戶可以通過編寫自己網站代碼得到這個token,然後用這個token立刻構造表單,發起CSRF攻擊,因此在js遍歷的時候,如果發現不是本站的鏈接,可以不加token。

在HTTP頭部增加屬性

這個方法在思路上和上面的token方式一樣,只不過將token放到了HTTP頭部中,不再參數傳遞,通過XMLHttpRequest類可以一次性的給所有請求加上csrftoken這個HTTP頭屬性並設置值。

這種方法適合上面批量添加token不方便的情況,一次性操作,不過局限性也比較大,XMLHttpRequest請求通常用在ajax方法中,並非所有請求都適合。

Yii2

首先要說的是每種CSRF防範措施都有其弊端,無論你的防範多麼嚴密,黑客擁有更多的攻擊手段,因此在重要邏輯上(必須寫入和刪除)必須非常小心,接下來我們把yii2框架在csrf上的部署說一下。

我們以yii2.0.14為解說版本。

在CSRF這塊,yii2框架採取了HTTP頭部和參數token並行的方式,針對於每個請求,在beforeAction都會做一次判斷,如下

// vendor/yiisoft/yii2/web/Controller.php
public function beforeAction($action) {

if (parent::beforeAction($action)) {
if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) {
throw new BadRequestHttpException(Yii::t("yii", "Unable to verify your data submission."));
}
return true;
}
return false;
}

如果我們沒有設置 enableCsrfValidation 為false,並且沒有報錯,則會進行csrf驗證,核心方法就是

Yii::$app->getRequest()->validateCsrfToken()

該方法存在於 vendor/yiisoft/yii2/web/Request.php 中,我們看一看它。

public function validateCsrfToken($clientSuppliedToken = null) {
// 省略上面代碼
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
}

validateCsrfToken函數代碼我們只需要看最後的返回,getBodyParam或getCsrfTokenFromHeader方法得到的token,只要有一種驗證通過,就認為合法。

以上是整體的思路,為了讓你看的更清晰,我畫一個圖並增加一些名詞解釋。

以上是yii2的csrf策略部署,當然我還是推薦你使用 xdebug等調試工具 一步一步看看這個過程。

最後我在把上圖的關鍵函數進行說明

  • generateCsrfToken() 該函數生成token並存到cookie或session中,該值不會隨頁面刷新而變化,它更多充當鑰匙的作用,根絕它生成具體的csrfToken。
  • getCsrfToken() 生成具體的csrfToken,就是你在表單隱藏域中看到的那個值,這個值將來會傳到伺服器和真實的csrfToken進行對比,驗證是否合法。
  • validateCsrfToken() 進行合法性驗證,該函數得到一個真實的csrfToken然後和客戶端上傳來的csrfToken進行對比。

小結

本來想來個很高逼格的小結,想想算了,華麗的辭彙不如下一篇靠譜的乾貨實在,等我下一篇哈。


感謝你能看到這裡 更多Yii2的知識

OSC開源社區頭條號,每日更新優質的技術類文章,包括外文翻譯,軟體更新,技術博客等。歡迎關注osc開源社區頭條號,每日獲取優質推送。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 OSC開源社區 的精彩文章:

Docker 創始人 Solomon Hykes 宣布從 Docker 離職

TAG:OSC開源社區 |