從開(kāi)發(fā)角度淺談CSRF攻擊及防御
什么是CSRF
CSRF可以叫做(跨站請(qǐng)求偽造),咱們可以這樣子理解CSRF,攻擊者可以利用你的身份你的名義去發(fā)送(請(qǐng)求)一段惡意的請(qǐng)求,從而導(dǎo)致可以利用你的賬號(hào)(名義)去--購(gòu)買(mǎi)商品、發(fā)郵件,惡意的去消耗賬戶(hù)資源,導(dǎo)致的一些列惡意行為.CSRF可以簡(jiǎn)單分為Get型和Post型兩種。
Get型CSRF:
看到這個(gè)名字,很明顯是發(fā)送GET請(qǐng)求導(dǎo)致的。我這里簡(jiǎn)單的說(shuō)一下:GET型的CSRF利用非常簡(jiǎn)單,通常只要發(fā)送一段HTTP請(qǐng)求。簡(jiǎn)單的說(shuō),如果一個(gè)網(wǎng)站某個(gè)地方的功能,比如(用戶(hù)修改自己郵箱)是通過(guò)GET進(jìn)行請(qǐng)求修改的話。
如下例:
/user.php?id=226&email=226@226.com //這里我們可以看出這個(gè)網(wǎng)址是通過(guò)get型去對(duì)用戶(hù)的郵箱進(jìn)行修改。后面&email=226@226.com 是關(guān)鍵的地方。當(dāng)我們發(fā)現(xiàn)沒(méi)有做任何處理之后,我們將可以去構(gòu)造一段代碼。
只要把它email參數(shù)后面的值進(jìn)行修改。。之后構(gòu)造一個(gè)代碼,或者直接一串URL發(fā)過(guò)去(正常人應(yīng)該不會(huì)這樣)。如下例:
/user.php?id=226&email=226@qq.com //只要這個(gè)id的用戶(hù)觸發(fā)了這URL即可成功修改。攻擊者可自行修改id,發(fā)送該id用戶(hù)也可以修改。觸發(fā)后即可成功修改到這個(gè)用戶(hù)的email。
POST型CSRF
POST型CSRF簡(jiǎn)單來(lái)說(shuō),通過(guò)POST請(qǐng)求去觸發(fā)代碼造成漏洞。還是一樣,舉個(gè)例子 比如在一個(gè)教育視頻網(wǎng)站平臺(tái)。在普通用戶(hù)的眼中,點(diǎn)擊網(wǎng)頁(yè)->打開(kāi)試看視頻->購(gòu)買(mǎi)視頻 是一個(gè)很正常的一個(gè)流程??墒窃诠粽叩难壑锌梢运阏?,但又不正常的,當(dāng)然不正常的情況下,是在開(kāi)發(fā)者安全意識(shí)不足沒(méi)有進(jìn)行處理所造成。攻擊者在購(gòu)買(mǎi)處抓到購(gòu)買(mǎi)時(shí)候網(wǎng)站處理購(gòu)買(mǎi)(扣除)用戶(hù)余額的地址。
比如:/coures/user/handler/25332/buy.php //通過(guò)buy.php處理購(gòu)買(mǎi)(購(gòu)買(mǎi)成功)的信息,這里的25532為視頻ID
那么攻擊者現(xiàn)在構(gòu)造一個(gè)表單(form.html),如:
document.forms[0].submit(); //自動(dòng)提交
構(gòu)造好form表單后,那么攻擊者將form.html上傳至一臺(tái)服務(wù)器上,將該頁(yè)面 如:/form.html
發(fā)送給受害者,只要受害者正在登陸當(dāng)前教育網(wǎng)站的時(shí)候,打開(kāi)攻擊者發(fā)送的頁(yè)面,那么代碼則自動(dòng)觸發(fā),自動(dòng)購(gòu)買(mǎi)了id為25332的視頻。從而導(dǎo)致受害者余額扣除,被攻擊者惡意消耗用戶(hù)余額。如果網(wǎng)站很不嚴(yán)謹(jǐn),那么只要把id改了,就可以任意的去惡意購(gòu)買(mǎi)任何視頻。消耗受害者的財(cái)產(chǎn),從而導(dǎo)致用戶(hù)財(cái)產(chǎn)安全受影響。
CSRF的原理
發(fā)現(xiàn)漏洞可利用處->構(gòu)造(搭建)搭建代碼->發(fā)送給用戶(hù)(管理員)->觸發(fā)代碼(發(fā)送請(qǐng)求).........
從這個(gè)利用的一個(gè)流程中,我們可以發(fā)現(xiàn),攻擊者僅僅只是做了兩處工作.第一處是:發(fā)現(xiàn)漏洞利用處,,第二處就是構(gòu)造利用代碼以及發(fā)送至用戶(hù)(管理員)。至于利用,你會(huì)發(fā)現(xiàn)CSRF與XSS不同,XSS是攻擊者自己提交,等待結(jié)果,而CSRF呢,是由用戶(hù)(管理員)自身提交。甚至可以說(shuō)攻擊者只做了構(gòu)造代碼的工作。
在開(kāi)發(fā)中如何簡(jiǎn)單防御CSRF(PHP)
其實(shí)防御CSRF有很多種 如:驗(yàn)證碼、驗(yàn)證Refer、以及驗(yàn)證token,對(duì)特殊參數(shù)進(jìn)行加密。
但是如果使用驗(yàn)證碼去避免CSRF的話,那么這樣會(huì)驗(yàn)證的影響用戶(hù)的體驗(yàn),因?yàn)橛脩?hù)不會(huì)每個(gè)操作都去輸入驗(yàn)證碼(會(huì)很煩)。
Refer的話在特殊情況下也是不靠譜的(服務(wù)器端出的問(wèn)題)。
那么目前只有token是被大多網(wǎng)站去使用的。因?yàn)榭梢员苊庥脩?hù)體驗(yàn)的問(wèn)題發(fā)生。同樣服務(wù)器邊問(wèn)題也發(fā)生也不會(huì)很多。
那么接下來(lái)就開(kāi)始介紹在PHP開(kāi)發(fā)中如何去簡(jiǎn)單的生成token,避免CSRF。我們可以通過(guò)PHP中函數(shù)(rand生成隨機(jī)數(shù)+uniqid生成一個(gè)唯一id+time時(shí)間戳)最后在講這幾個(gè)生成的值用md5加密。接下來(lái)來(lái)說(shuō)說(shuō)如何去生產(chǎn):
首先先開(kāi)啟session會(huì)話
session_start(); //開(kāi)啟session會(huì)話
然后我們?nèi)ルS機(jī)生成一段值(這個(gè)值就是我們的token值) 備注:其實(shí)這樣子生成不是最嚴(yán)謹(jǐn)?shù)?此次只是大家一起交流。大家可以去嘗試各種方式。)
$safe226 = md5(time() . uniqid() . rand(1,99999)); //輸入一個(gè)隨機(jī)數(shù)值
我們輸出看看
接下來(lái),我們需要做的就是把生成出來(lái)的token丟進(jìn)咱們的session里面。
接下來(lái)你們應(yīng)該知道了,我們驗(yàn)證的其實(shí)是我們存到session里面的token是否與用戶(hù)提交上來(lái)的token值一致。如果一致則成功,否則則失敗。我們準(zhǔn)備一個(gè)表單,用于傳遞用戶(hù)提交請(qǐng)求的一個(gè)token。
">
我們把token提交到test.php里去處理!
其實(shí)就是將我們丟進(jìn)session里面的值丟進(jìn)隱藏表單里面。當(dāng)用戶(hù)提交的時(shí)候一起提交過(guò)來(lái)驗(yàn)證,驗(yàn)證是否與session里面的token相同。
我們來(lái)感受下。
{C}
Ok,接下來(lái)我們只需要去判斷用戶(hù)傳遞過(guò)來(lái)的token值是否和session里面的值一致(這里使用簡(jiǎn)單判斷,if(isset($_SESSION['226_token']) && isset($_POST['token'])){
if($_SESSION['226_token'] === $_POST['token']){
//這里是驗(yàn)證成功后所寫(xiě)代碼
echo '購(gòu)買(mǎi)成功';
}else{
echo '請(qǐng)勿非法操作!判斷是否一致';
}
}else{
echo '請(qǐng)勿非法操作!判斷是否存在';
}
接下來(lái),我們利用下。這里已知道結(jié)構(gòu)。所以直接構(gòu)造一個(gè)表單。
根據(jù)代碼情況 就是當(dāng)我們構(gòu)造的利用代碼,沒(méi)有傳遞token或者token不一致的時(shí)候:
這里兩個(gè)打印是在test.php里面打印 沒(méi)有去掉print_r(無(wú)視就好),當(dāng)利用時(shí)候會(huì)發(fā)現(xiàn)沒(méi)有傳遞token過(guò)去。會(huì)提示。錯(cuò)誤!
接下來(lái)我們來(lái)看,完全一致的時(shí)候。會(huì)提示的是什么:
當(dāng)我們通過(guò)驗(yàn)證的時(shí)候。你會(huì)發(fā)現(xiàn)已經(jīng)驗(yàn)證成功。購(gòu)買(mǎi)成功。