58金融的CSRF防禦實踐


導讀

防範CSRF攻擊對於互聯網企業來說意義重大,本文結合58金融的實踐場景,旨在幫助大家共同提高nodejs服務的安全性。


背景

Web端的跨站點請求偽造(Cross Site Request Forgery)攻擊,簡稱CSRF攻擊,存在巨大的危害性。CSRF裡,攻擊者借用了受害者的身份對服務器發送請求,對服務器來說這個請求是完全合法的,但是卻完成了攻擊者所期望的一個操作:危害小的如以受害者的名義發帖子,危害大的可以冒用受害者的賬號下單甚至轉賬。

舉個例子,受害者無意訪問到了某個邪惡網頁,該網頁裡只需要定義一個圖片:

<code>/<code> 

即可達到以受害者的名義向helloback的服務器發送該請求。如果恰好受害者之前訪問過hellobank,受害者的合法cookie很有可能會被自動帶上一起發送給hellobank服務器,進而通過後者的身份校驗,最終轉賬100元給6225賬號。

上面只是一個簡單偽造get請求的例子,事實上post請求也可以同樣偽造,而且攻擊方式也更為複雜。例如,CSRF結合XSS,受害者沒有訪問過任何邪惡網頁的前提下也會被CSRF攻擊成功。

由上可見,CSRF攻擊的危害性極大,特別是對於金融業務,很多接口都是和貨幣產品相關,被攻擊成功的話後果非常嚴重。58金融的全部web流量都由nodejs承接,由nodejs負責防禦web相關的安全攻擊。針對CSRF攻擊,我們建立了一套專門的防禦方案,在此分享給大家,共同提高web服務安全。

常見認知誤區

網上有很多文章介紹使用Http請求頭的referrer字段檢測是否CSRF,以及介紹使用Post請求代替Get請求來防禦CSRF。其實這些手段僅僅是增加了CSRF攻擊的難度,並不能真正意義上防禦。

想要100%防禦CSRF攻擊,不僅涉及到後端(服務器)的工作,也涉及到前端(瀏覽器)的JavaScript代碼改造。而且更為重要的是,光靠技術手段遠遠不夠,更需要前後端達成並遵守一套開發規則,才能徹底杜絕CSRF攻擊。


開發規則

我們在眾多實際項目中總結出如下6條規則。

規則1:get請求無需防禦CSRF攻擊。

按照HTTP語義來說,get請求僅用於查詢,不能用於提交信息。也就是說任何一個get請求,都不應該導致後端業務狀態及業務數據的變化。攻擊者一定是希望通過CSRF攻擊造成後端業務數據的變化,如發帖購物轉賬等,沒有變化也就無需防禦。(接口防惡意刷數的除外,不在本文的討論範圍)

該規則雖看似簡單,實際開發中卻常被接口制定者所忽略。在實際開發中我們見到很多開發中使用get請求提交信息,或者更為隱蔽的漏洞是,雖然get請求沒有提交任何信息,但卻導致了後端服務狀態或數據庫數據發生了改變。因此該規則的重點在於後端同學正確理解HTTP語義和定義前後端接口。

規則2:不攜帶業務cookie的請求無需防禦

這條規則看似簡單,但往往最容易被開發人員所忽略。CSRF的攻擊者一定是希望冒用受害者的身份,通常更準確的術語是cookie,去發送某些請求到服務器以達到攻擊者的目的。但如果受害者連業務cookie都沒有的話,說明服務器根本不認識該受害者,攻擊者的目的也就無法實現了。換句話理解:用戶都沒登錄,不為系統識別,模擬他沒有任何收益。(接口防惡意刷數的除外,不在本文的討論範圍)

舉個例子,新聞網站的列表頁和詳情頁,一般都開放給所有人查看。攻擊者誘使其他非登錄用戶訪問某個新聞頁面,沒有收益且不會導致新聞網站後臺的業務和狀態數據改變,因此一般來講無需防範。(當然如果考慮到點擊量和曝光率、廣告費的話也有必要防禦,這些不在本文討論範圍)

規則3:URL白名單裡的post請求無需防範

業務中總會有一些接口,必須設置為禁止防禦CSRF攻擊。比如第三方的回調請求,一般都是對方服務器直接發起請求到我們的服務器,根本沒有經過瀏覽器,因此肯定無法通過CSRF防禦。這類請求一般是靠固定IP、業務token頒發的方式進行安全校驗。我們在服務端不做CSRF檢測和防禦。很多銀行類接口如轉賬,以及微信公眾號配置服務器,這類請求經常需要銀行服務器或微信服務器異步回調我們的業務服務器,因此必須配置白名單,繞過CSRF檢測。

規則4:瀏覽器端發送post請求時,為header添加csrf參數,其值由業務cookie計算得出

規則5:服務器端收到post請求時,檢查其業務cookie及header中的csrf是否正確匹配

為什麼最後這倆條規則要放一起呢?因為這倆條規則是CSRF防禦的技術核心,前後端代碼配合一起作用,才能防禦CSRF攻擊。單獨僅前端或後端防禦肯定行不通。

首先,為什麼要給請求增加header呢?因為受害者在訪問邪惡網頁時,受害者向我們的服務器所發出的請求,該請求和邪惡網頁的域名一定是不同的,這也是CSRF中的Cross-Site的含義。因此受到跨域的限制,攻擊者無法改變該請求的任何信息,特別是受害者的業務cookie值。

所以我們在瀏覽器端,給合法用戶請求的header上加上csrf的參數,並且該參數的值由業務cookie計算得出。如此則攻擊者無法事先知道受害者的cookie,也就無法計算出header的csrf參數。

然後在服務器端獲取業務cookie以及header中的csrf值進行匹配校驗,一致則認為是有效請求,不一致則認為是CSRF攻擊進行攔截。

規則6:可以使用專門的CSRF cookie替換業務cookie,但要保證cookie足夠隨機、無法被預測

針對某些網站,其業務cookie因安全原因或其他歷史原因設置為httponly,導致JavaScript無法讀取。此時我們需要在nodejs端生成一個可以被JavaScript讀取的cookie,專門負責處理CSRF邏輯。


具體實現

上述規則可以用如下代碼邏輯實現(代碼僅供邏輯展示,均已脫敏僅供參考)。

這裡我們選用了koa2,將判斷邏輯抽象成一個函數,返回true時代表截獲到了CSRF攻擊。

58金融的CSRF防禦實踐

然後在業務代碼裡應用該方法,對CSRF攻擊返回403狀態碼。需要注意的是根據koa2的洋蔥模型,下面代碼應該放置在post路由中間件之前。

58金融的CSRF防禦實踐

這裡可以根據業務需要,適當拓展防禦措施,如根據CSRF攻擊的頻次考慮增加校驗碼流程,或對IP進行限制。此處不做詳述。


作者簡介:

賈建容,58金融前端負責人,主要負責金融中後臺系統架構和建設。


分享到:


相關文章: