Csrf漏洞
CSRF(Cross-site request forgery)跨站請(qǐng)求偽造:也被稱為“One Click Attack”或者Session Riding,通成性常縮寫為CSRF或者XSRF忆嗜,是一種對(duì)網(wǎng)站的惡意利用跪妥。盡管聽起來像跨站腳本(XSS),但它與XSS非常不同,XSS利用站點(diǎn)內(nèi)的信任用戶鉴扫,而CSRF則通過偽裝來自受信任用戶的請(qǐng)求來利用受信任的網(wǎng)站身害。與XSS攻擊相比味悄,CSRF攻擊往往不大流行(因此對(duì)其進(jìn)行防范的資源也相當(dāng)稀少)和難以防范,所以被認(rèn)為比XSS更具危險(xiǎn)性塌鸯。
Csrf漏洞分析
成因
其實(shí)說白了侍瑟,csrf漏洞的成因就是網(wǎng)站的cookie在瀏覽器中不會(huì)過期,只要不關(guān)閉瀏覽器或者退出登錄丙猬,那以后只要是訪問這個(gè)網(wǎng)站涨颜,都會(huì)默認(rèn)你已經(jīng)登錄的狀態(tài)。而在這個(gè)期間茧球,攻擊者發(fā)送了構(gòu)造好的csrf腳本或包含csrf腳本的鏈接庭瑰,可能會(huì)執(zhí)行一些用戶不想做的功能(比如是添加賬號(hào)等)。這個(gè)操作不是用戶真正想要執(zhí)行的袜腥。
危害
攻擊者盜用了你的身份见擦,以你的名義發(fā)送惡意請(qǐng)求。CSRF能夠做的事情包括:以你名義發(fā)送郵件羹令,發(fā)消息鲤屡,盜取你的賬號(hào),甚至于購(gòu)買商品福侈,虛擬貨幣轉(zhuǎn)賬……造成的問題包括:個(gè)人隱私泄露以及財(cái)產(chǎn)安全酒来。
示例
首先找到一個(gè)目標(biāo)站點(diǎn),csrf存在的危害主要存在于可以執(zhí)行操作的地方肪凛,那么我在我搭建的一個(gè)環(huán)境中的登錄后頁(yè)面進(jìn)行測(cè)試環(huán)境就是一個(gè)wordpress的環(huán)境堰汉,大家可以直接去官網(wǎng)下載
我們選擇用戶界面進(jìn)行測(cè)試,可以看到現(xiàn)在只有一個(gè)用戶
下面我添加用戶
利用burp進(jìn)行截?cái)?/p>
利用burp的自帶插件來利用csrf
會(huì)生成一個(gè)可以利用csrf.html
修改標(biāo)注內(nèi)的值伟墙,來保證添加的用戶不會(huì)重復(fù)造成無法添加
在瀏覽器中嘗試
執(zhí)行按鍵翘鸭,發(fā)現(xiàn)除了本來存在的第一個(gè)用戶和我們通過正常手段加入的用戶雙增加了一個(gè)新的test1用戶,這個(gè)用戶就是我們利用csrf點(diǎn)擊圖片中的submit來執(zhí)行的操作戳葵,因?yàn)槭俏覀兊臏y(cè)試沒有對(duì)頁(yè)面進(jìn)行修改和直接觸發(fā)就乓,如果是攻擊者利用JS來讓用戶進(jìn)行直接的觸發(fā),只要打開了相應(yīng)的頁(yè)面就會(huì)執(zhí)行這一行為。
Csrf高級(jí)應(yīng)用
利用html很不容易讓人利用生蚁,漏洞觸發(fā)復(fù)雜噩翠,那我們就想辦法讓這個(gè)觸發(fā)方式變的簡(jiǎn)單起來。
利用xss漏洞來觸發(fā)csrf漏洞邦投,完成用戶添加的操作伤锚。
我們首先要先了解發(fā)送的數(shù)據(jù)包內(nèi)容
創(chuàng)建一個(gè)csrf的項(xiàng)目,我們來編寫一下我們的代碼吧
把這段代碼粘到項(xiàng)目的代碼配置中去
然后把我們的可利用代碼通過留言的存儲(chǔ)型XSS漏洞存入到我的目標(biāo)站點(diǎn)中去
留言成功后的效果如下
當(dāng)管理員查看留言時(shí)就執(zhí)行了我們的危險(xiǎn)代碼并發(fā)送了添加用戶的請(qǐng)求
在查看用戶列表成功的加入了test2用戶
到這里志衣,csrf的攻擊實(shí)例可以說講的差不多了屯援,以后就要大家自己去挖掘了。
利用工具介紹
Burp相信大家也都有一定的了解蠢涝,截?cái)鄶?shù)據(jù)包玄呛,構(gòu)造csrf漏洞poc,基本是最簡(jiǎn)單方便的工具了和二。
在給大家介紹一個(gè)CSRFTester-1.0徘铝,這個(gè)工具,專門針對(duì)csrf漏洞惯吕,可以構(gòu)造多種樣式的poc
雙擊bat文件運(yùn)行
設(shè)置瀏覽器8008代理惕它,點(diǎn)start就可以獲取到通過的數(shù)據(jù)包了
最下方構(gòu)造需要的poc加以利用,總的來說比burp更加專業(yè)废登,如果專門利用csrf可以使用這個(gè)工具淹魄。
總結(jié)
漏洞依靠用戶標(biāo)識(shí)危害網(wǎng)站,利用網(wǎng)站對(duì)用戶標(biāo)識(shí)的信任堡距,欺騙用戶的瀏覽器發(fā)送HTTP請(qǐng)求給目標(biāo)站點(diǎn)甲锡,另外可以通過IMG標(biāo)簽會(huì)觸發(fā)一個(gè)GET請(qǐng)求,可以利用它來實(shí)現(xiàn)CSRF攻擊羽戒。
CSRF攻擊依賴下面的假定:
攻擊者了解受害者所在的站點(diǎn)
攻擊者的目標(biāo)站點(diǎn)具有持久化授權(quán)cookie或者受害者具有當(dāng)前會(huì)話cookie
目標(biāo)站點(diǎn)沒有對(duì)用戶在網(wǎng)站行為的第二授權(quán)
1 缤沦、CSRF攻擊原理
CSRF攻擊原理比較簡(jiǎn)單,如圖1所示易稠。其中Web A為存在CSRF漏洞的網(wǎng)站缸废,Web B為攻擊者構(gòu)建的惡意網(wǎng)站,User C為Web A網(wǎng)站的合法用戶驶社。
1. 用戶C打開瀏覽器企量,訪問受信任網(wǎng)站A,輸入用戶名和密碼請(qǐng)求登錄網(wǎng)站A亡电;
2.在用戶信息通過驗(yàn)證后届巩,網(wǎng)站A產(chǎn)生Cookie信息并返回給瀏覽器,此時(shí)用戶登錄網(wǎng)站A成功份乒,可以正常發(fā)送請(qǐng)求到網(wǎng)站A姆泻;
3. 用戶未退出網(wǎng)站A之前零酪,在同一瀏覽器中冒嫡,打開一個(gè)TAB頁(yè)訪問網(wǎng)站B拇勃;
4. 網(wǎng)站B接收到用戶請(qǐng)求后,返回一些攻擊性代碼孝凌,并發(fā)出一個(gè)請(qǐng)求要求訪問第三方站點(diǎn)A方咆;
5. 瀏覽器在接收到這些攻擊性代碼后,根據(jù)網(wǎng)站B的請(qǐng)求蟀架,在用戶不知情的情況下攜帶Cookie信息瓣赂,向網(wǎng)站A發(fā)出請(qǐng)求。網(wǎng)站A并不知道該請(qǐng)求其實(shí)是由B發(fā)起的片拍,所以會(huì)根據(jù)用戶C的Cookie信息以C的權(quán)限處理該請(qǐng)求煌集,導(dǎo)致來自網(wǎng)站B的惡意代碼被執(zhí)行。
2捌省、 CSRF漏洞防御
CSRF漏洞防御主要可以從三個(gè)層面進(jìn)行苫纤,即服務(wù)端的防御、用戶端的防御和安全設(shè)備的防御纲缓。
2.1 ? ? ?服務(wù)端的防御
2.1.1 ?驗(yàn)證HTTP Referer字段
根據(jù)HTTP協(xié)議卷拘,在HTTP頭中有一個(gè)字段叫Referer,它記錄了該HTTP請(qǐng)求的來源地址祝高。在通常情況下栗弟,訪問一個(gè)安全受限頁(yè)面的請(qǐng)求必須來自于同一個(gè)網(wǎng)站。比如某銀行的轉(zhuǎn)賬是通過用戶訪問http://bank.test/test?page=10&userID=101&money=10000頁(yè)面完成工闺,用戶必須先登錄bank.test乍赫,然后通過點(diǎn)擊頁(yè)面上的按鈕來觸發(fā)轉(zhuǎn)賬事件。當(dāng)用戶提交請(qǐng)求時(shí)陆蟆,該轉(zhuǎn)賬請(qǐng)求的Referer值就會(huì)是轉(zhuǎn)賬按鈕所在頁(yè)面的URL(本例中雷厂,通常是以bank. test域名開頭的地址)。而如果攻擊者要對(duì)銀行網(wǎng)站實(shí)施CSRF攻擊遍搞,他只能在自己的網(wǎng)站構(gòu)造請(qǐng)求罗侯,當(dāng)用戶通過攻擊者的網(wǎng)站發(fā)送請(qǐng)求到銀行時(shí),該請(qǐng)求的Referer是指向攻擊者的網(wǎng)站溪猿。因此钩杰,要防御CSRF攻擊,銀行網(wǎng)站只需要對(duì)于每一個(gè)轉(zhuǎn)賬請(qǐng)求驗(yàn)證其Referer值诊县,如果是以bank. test開頭的域名讲弄,則說明該請(qǐng)求是來自銀行網(wǎng)站自己的請(qǐng)求,是合法的依痊。如果Referer是其他網(wǎng)站的話避除,就有可能是CSRF攻擊怎披,則拒絕該請(qǐng)求。
2.1.2 在請(qǐng)求地址中添加token并驗(yàn)證
CSRF攻擊之所以能夠成功瓶摆,是因?yàn)楣粽呖梢詡卧煊脩舻恼?qǐng)求凉逛,該請(qǐng)求中所有的用戶驗(yàn)證信息都存在于Cookie中,因此攻擊者可以在不知道這些驗(yàn)證信息的情況下直接利用用戶自己的Cookie來通過安全驗(yàn)證群井。由此可知状飞,抵御CSRF攻擊的關(guān)鍵在于:在請(qǐng)求中放入攻擊者所不能偽造的信息,并且該信息不存在于Cookie之中书斜。鑒于此诬辈,系統(tǒng)開發(fā)者可以在HTTP請(qǐng)求中以參數(shù)的形式加入一個(gè)隨機(jī)產(chǎn)生的token,并在服務(wù)器端建立一個(gè)攔截器來驗(yàn)證這個(gè)token荐吉,如果請(qǐng)求中沒有token或者token內(nèi)容不正確焙糟,則認(rèn)為可能是CSRF攻擊而拒絕該請(qǐng)求。
2.1.3 在HTTP頭中自定義屬性并驗(yàn)證
自定義屬性的方法也是使用token并進(jìn)行驗(yàn)證样屠,和前一種方法不同的是穿撮,這里并不是把token以參數(shù)的形式置于HTTP請(qǐng)求之中,而是把它放到HTTP頭中自定義的屬性里瞧哟。通過XMLHttpRequest這個(gè)類混巧,可以一次性給所有該類請(qǐng)求加上csrftoken這個(gè)HTTP頭屬性,并把token值放入其中勤揩。這樣解決了前一種方法在請(qǐng)求中加入token的不便咧党,同時(shí),通過這個(gè)類請(qǐng)求的地址不會(huì)被記錄到瀏覽器的地址欄陨亡,也不用擔(dān)心token會(huì)通過Referer泄露到其他網(wǎng)站傍衡。
3 其他防御方法
1. ?CSRF攻擊是有條件的,當(dāng)用戶訪問惡意鏈接時(shí)负蠕,認(rèn)證的cookie仍然有效蛙埂,所以當(dāng)用戶關(guān)閉頁(yè)面時(shí)要及時(shí)清除認(rèn)證cookie,對(duì)支持TAB模式(新標(biāo)簽打開網(wǎng)頁(yè))的瀏覽器尤為重要遮糖。
2. ?盡量少用或不要用request()類變量绣的,獲取參數(shù)指定request.form()還是request. querystring (),這樣有利于阻止CSRF漏洞攻擊欲账,此方法只不能完全防御CSRF攻擊屡江,只是一定程度上增加了攻擊的難度。
Java?代碼示例
下文將以 Java 為例赛不,對(duì)上述三種方法分別用代碼進(jìn)行示例惩嘉。無論使用何種方法,在服務(wù)器端的攔截器必不可少踢故,它將負(fù)責(zé)檢查到來的請(qǐng)求是否符合要求文黎,然后視結(jié)果而決定是否繼續(xù)請(qǐng)求或者丟棄惹苗。在 Java 中,攔截器是由 Filter 來實(shí)現(xiàn)的耸峭。我們可以編寫一個(gè) Filter桩蓉,并在 web.xml 中對(duì)其進(jìn)行配置,使其對(duì)于訪問所有需要 CSRF 保護(hù)的資源的請(qǐng)求進(jìn)行攔截抓艳。
在 filter 中對(duì)請(qǐng)求的 Referer 驗(yàn)證代碼如下
清單 1. 在 Filter 中驗(yàn)證 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);
?}?
以上代碼先取得 Referer 值触机,然后進(jìn)行判斷,當(dāng)其非空并以 bank.example 開頭時(shí)玷或,則繼續(xù)請(qǐng)求,否則的話可能是 CSRF 攻擊片任,轉(zhuǎn)到 error.jsp 頁(yè)面偏友。
如果要進(jìn)一步驗(yàn)證請(qǐng)求中的 token 值,代碼如下
清單 2. 在 filter 中驗(yàn)證請(qǐng)求中的 token
HttpServletRequest req = (HttpServletRequest)request;
?HttpSession s = req.getSession();
?// 從 session 中得到 csrftoken 屬性
?String sToken = (String)s.getAttribute(“csrftoken”);
?if(sToken == null){
? ? // 產(chǎn)生新的 token 放入 session 中
? ? sToken = generateToken();
? ? s.setAttribute(“csrftoken”,sToken);
? ? chain.doFilter(request, response);
?} else{
? ? // 從 HTTP 頭中取得 csrftoken
? ? String xhrToken = req.getHeader(“csrftoken”);
? ? // 從請(qǐng)求參數(shù)中取得 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对供,如果沒有位他,則認(rèn)為是第一次訪問,session 是新建立的产场,這時(shí)生成一個(gè)新的 token鹅髓,放于 session 之中,并繼續(xù)執(zhí)行請(qǐng)求京景。如果 session 中已經(jīng)有 csrftoken窿冯,則說明用戶已經(jīng)與服務(wù)器之間建立了一個(gè)活躍的 session,這時(shí)要看這個(gè)請(qǐng)求中有沒有同時(shí)附帶這個(gè) token确徙,由于請(qǐng)求可能來自于常規(guī)的訪問或是 XMLHttpRequest 異步訪問醒串,我們分別嘗試從請(qǐng)求中獲取 csrftoken 參數(shù)以及從 HTTP 頭中獲取 csrftoken 自定義屬性并與 session 中的值進(jìn)行比較,只要有一個(gè)地方帶有有效 token鄙皇,就判定請(qǐng)求合法芜赌,可以繼續(xù)執(zhí)行,否則就轉(zhuǎn)到錯(cuò)誤頁(yè)面伴逸。生成 token 有很多種方法缠沈,任何的隨機(jī)算法都可以使用,Java 的 UUID 類也是一個(gè)不錯(cuò)的選擇错蝴。
除了在服務(wù)器端利用 filter 來驗(yàn)證 token 的值以外洲愤,我們還需要在客戶端給每個(gè)請(qǐng)求附加上這個(gè) token,這是利用 js 來給 html 中的鏈接和表單請(qǐng)求地址附加 csrftoken 代碼漱竖,其中已定義 token 為全局變量禽篱,其值可以從 session 中得到。
清單 3. 在客戶端對(duì)于請(qǐng)求附加 token
function appendToken(){
? ? updateForms();
? ? updateTags();
?}
?function updateForms() {
? ? // 得到頁(yè)面中所有的 form 元素
? ? var forms = document.getElementsByTagName('form');
? ? for(i=0; i<forms.length; i++) {
? ? ? ? var url = forms[i].action;
? ? ? ? // 如果這個(gè) form 的 action 值為空馍惹,則不附加 csrftoken
? ? ? ? if(url == null || url == "" ) continue;
? ? ? ? // 動(dòng)態(tài)生成 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 中含有只相當(dāng)頁(yè)的錨標(biāo)記
? ? ? ? ? ? fragment = location.substring(fragmentIndex);
? ? ? ? ? ? location = location.substring(0,fragmentIndex);
? ? ? ? }
? ? ? ? var index = location.indexOf('?');
? ? ? ? if(index != -1) {
? ? ? ? ? ? //url 中已含有其他參數(shù)
? ? ? ? ? ? location = location + '&csrftoken=' + token;
? ? ? ? } else {
? ? ? ? ? ? //url 中沒有其他參數(shù)
? ? ? ? ? ? location = location + '?csrftoken=' + token;
? ? ? ? }
? ? ? ? if(fragment != null){
? ? ? ? ? ? location += fragment;
? ? ? ? }
? ? ? ? element.setAttribute(attr, location);
? ? }
?}?
在客戶端 html 中玛界,主要是有兩個(gè)地方需要加上 token,一個(gè)是表單 form悼吱,另一個(gè)就是鏈接 a慎框。這段代碼首先遍歷所有的 form,在 form 最后添加一隱藏字段后添,把 csrftoken 放入其中笨枯。然后,代碼遍歷所有的鏈接標(biāo)記 a遇西,在其 href 屬性中加入 csrftoken 參數(shù)馅精。注意對(duì)于 a.href 來說,可能該屬性已經(jīng)有參數(shù)粱檀,或者有錨標(biāo)記洲敢。因此需要分情況討論,以不同的格式把 csrftoken 加入其中茄蚯。