也談 CSRF

國際慣例,先來一波名詞解釋(來自維基百科):

CSRF: 跨站請求偽造(corss-site request forgery) 也被稱為 one-click 或 session riding,也可稱為 XSRF。是一種挾持用戶在當(dāng)前已登錄的 web 應(yīng)用程序上執(zhí)行非本意的操作的攻擊方法。跟 XSS(跨站腳本攻擊) 相比,XSS 利用的是用戶對指定網(wǎng)站的信任,而 CSRF 利用的是網(wǎng)站對用戶瀏覽器的信任。

先來談?wù)勎覟槭裁匆獙戇@篇文章。主要原因有二:

? 1)? 內(nèi)容過舊;

? 2)? 關(guān)鍵點(diǎn)講的不夠詳細(xì),比如:禁用 CORS 可以防范 CSRF。初看有一種一臉懵逼的感覺(也有可能我太菜雞???);

另外,起初了解時,跟朋友聊到這個話題,發(fā)現(xiàn)朋友對 CSRF 存在很大的誤解:

?某不愿透露姓名的朋友對 CSRF 的認(rèn)知

這里強(qiáng)調(diào)一下:CSRF 的目的并非竊取用戶的身份信息!因?yàn)?CSRF 攻擊者根本拿不到受害者的任何信息。

接下來切入正題。

如何確認(rèn)應(yīng)用是否存在 CSRF 漏洞?

在談如何確認(rèn)是否存在該漏洞之前,我們先來了解一次成功的 CSRF 攻擊成功所必要的前提條件:

????1)? 已在攻擊目標(biāo)站點(diǎn)登錄授權(quán),且應(yīng)用中使用了 form 表單發(fā)起會造成數(shù)據(jù)更改的請求;

????2)? 不規(guī)范的使用 HTTP 方法,如何定義不規(guī)范使用? 如:使用 get 請求修改數(shù)據(jù);

當(dāng)然, 還依賴你的登錄環(huán)境以及你沒有做 CSRF 防御這個前提。

有了這兩個前提條件,那我們?nèi)ザㄎ豢赡艽嬖趩栴}的代碼就簡單很多了。我們只需要應(yīng)用中是否存在 form 提交數(shù)據(jù)(注意,是提交數(shù)據(jù),不是加載數(shù)據(jù)。即該類請求會造成數(shù)據(jù)庫數(shù)據(jù)的的更改);再者,檢查應(yīng)用中是否有 GET、HEAD 請求造成數(shù)據(jù)庫數(shù)據(jù)更改的接口。?

如何防御 CSRF?

先來講幾個網(wǎng)上普遍說的幾種方案:

1. 驗(yàn)證 HTTP Referer

????這算是最簡單快速的防范 CSRF 的方法,原理很簡單:

即在處理請求之前,檢查 referer 值是否站點(diǎn)本身的 hosts, 如果不是,則拒絕此次請求。你可能會說在一些低版本的瀏覽器中是存在可以篡改 referer 的方法的。 在這里我并不打算談 IE6、firefox2 存在篡改 referer 方法, 畢竟這些都是過去式, 在當(dāng)今的 web 環(huán)境下,根本不需要考慮這個問題。那是不是可以認(rèn)為這是個既簡單又完美的方案呢?答案是否定的:

????1)? 在 firefox 中,可以設(shè)置禁用 referer,具體原因與操作方法如果你愿意了解,可以點(diǎn)我

????2)? 嗯...并且 Opera 也有這個功能, 操作方法點(diǎn)我,并搜索文本 “Referrer logging” 檢索到內(nèi)容

????3) chrome 也允許以修改啟動項(xiàng)參數(shù)的方式禁用 referer,當(dāng)然,這么干的人也不是什么電腦文盲。具體操作方式點(diǎn)我

看到上面的 1 2 3,你還敢使用這 簡單、“完美”作為你的應(yīng)用的防范 CSRF 的方案嗎?反正我不敢。

2. CSRF token

這種防范機(jī)制, 結(jié)合我自己的理解, 大概可以描述為(本想畫成圖的,無奈作圖水平太差,只好以文字表達(dá)):

每次頁面渲染時,往頁面某個元素上寫入 token, 比如 <input id="J_csrfToken" type="hidden"? value="{{csrfToken}}" />,并為頁面上所有的 a.href都加上 csrfToken 的 qs 參數(shù),而對于 form, 則在表單域內(nèi)創(chuàng)建 <input type="hidden" name="csrf-token" value="{{csrfToken}}" />??,如此一來,當(dāng)出發(fā) a.href 或 form.submit 時, csrfToken 都以 qs 的方式發(fā)送給服務(wù)端, 服務(wù)端根據(jù)該 token 進(jìn)行校驗(yàn)確定是否為合法請求。 而對于動態(tài)生成的 html 片段, 則通過 document.getElementById('J_csrfToken').value 的方式手動編碼傳輸給服務(wù)端。

那我們何時需要采用這種方法來防御 CSRF?你只要需要確認(rèn):

? ? 1)? 是否采用有副作用的 GET、HEAD 請求

? ? 2)? 是否有采用 form.submit 的方式修改數(shù)據(jù)庫數(shù)據(jù)

如果有的話,那你可以考慮采用這種方式來防御

3. 禁用 CORS

實(shí)驗(yàn)證明,簡單的禁用 CORS 并不能有效防御 CSRF,它需要一些前提:

? ? 1)正確的使用了 HTTP 方法, 不要使 GET、 HEAD 有副作用;

? ? 2)如果你使用了有副作用的 GET, 攻擊者仍然可以通過創(chuàng)建 script 標(biāo)簽再指定 src 為對應(yīng)的請求即可完成攻擊;

? ? 3)如果你使用了有副作用的 GET且沒有做登錄驗(yàn)證,那么使用 xhr 也能輕松完成攻擊,畢竟 CORS 屬于瀏覽器的安全策略, 它只攔截響應(yīng)而非請求;

其他的一些文章中, 也有講到一些其他的方案, 但我覺得沒有實(shí)踐價值,就沒必要贅述(其實(shí)我覺得這里列出來的,也沒啥實(shí)踐價值。只是整體消化下來,感覺總有一些點(diǎn)沒講清楚,就作為補(bǔ)充來描述一下)。接下來談?wù)剛€人理解的防范之道:

1. 正確使用 HTTP 方法;

2. 不要使 GET、HEAD 有副作用;

3. 存在 form 表單提交數(shù)據(jù)的場景時,可以使用 csrfToken 方案;

做到以上幾點(diǎn), 我相信基本可以將 CSRF 拒之門外。

如何發(fā)起一次 CSRF 攻擊?

根據(jù)一次成功的 CSRF 攻擊所需要的必備條件, 首先當(dāng)然是需要攻擊目標(biāo)在站點(diǎn)登錄。

ok, 接下來腦補(bǔ)一下:作為攻擊者的你,做了一個精心準(zhǔn)備的算命網(wǎng)站,上面一個醒目大按鈕——“測一測”,點(diǎn)擊時,其實(shí)是發(fā)起了一個 form.submit ,將必要的字段都放置其中,坐等受害者上鉤。一旦用戶點(diǎn)擊, 你將以受害者的名義以你想要的結(jié)果修改了受害者的數(shù)據(jù),達(dá)成攻擊目標(biāo)。

以上,希望對你有幫助!另,也歡迎勘誤!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容