PHP开发中csrf攻击的简单演示和防范
csrf攻擊,即cross site request forgery跨站(域名)請(qǐng)求偽造,這里的forgery就是偽造的意思。網(wǎng)上有很多關(guān)于csrf的介紹,比如一位前輩的文章CSRF的攻擊方式詳解,參考這篇文章簡(jiǎn)單解釋下:csrf 攻擊能夠?qū)崿F(xiàn)依賴(lài)于這樣一個(gè)簡(jiǎn)單的事實(shí):我們?cè)谟脼g覽器瀏覽網(wǎng)頁(yè)時(shí)通常會(huì)打開(kāi)好幾個(gè)瀏覽器標(biāo)簽(或窗口),假如我們登錄了一個(gè)站點(diǎn)A,站點(diǎn)A如果是通過(guò)cookie來(lái)跟蹤用戶(hù)的會(huì)話,那么在用戶(hù)登錄了站點(diǎn)A之后,站點(diǎn)A就會(huì)在用戶(hù)的客戶(hù)端設(shè)置cookie,假如站點(diǎn)A有一個(gè)頁(yè)面siteA-page.php(url資源)被站點(diǎn)B知道了url地址,而這個(gè)頁(yè)面的地址以某種方式被嵌入到了B站點(diǎn)的一個(gè)頁(yè)面siteB-page.php中,如果這時(shí)用戶(hù)在保持A站點(diǎn)會(huì)話的同時(shí)打開(kāi)了B站點(diǎn)的siteB-page.php,那么只要siteB-page.php頁(yè)面可以觸發(fā)這個(gè)url地址(請(qǐng)求A站點(diǎn)的url資源)就實(shí)現(xiàn)了csrf攻擊。
上面的解釋很拗口,下面舉個(gè)簡(jiǎn)單的例子來(lái)演示下。
1,背景和正常的請(qǐng)求流程
A站點(diǎn)域名為html5.yang.com,它有一個(gè)/get-update.php?uid=uid&username=username地址,可以看到這個(gè)地址可以通過(guò)get方法來(lái)傳遞一些參數(shù),假如這個(gè)頁(yè)面的邏輯是:它通過(guò)判斷uid是否合法來(lái)更新username,這個(gè)頁(yè)面腳本如下:
?
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php // 這里簡(jiǎn)便起見(jiàn), 從data.json中取出數(shù)據(jù)代替請(qǐng)求數(shù)據(jù)庫(kù) $str = file_get_contents('data.json'); $data = json_decode($str, true); // 檢查cookie和請(qǐng)求更改的uid, 實(shí)際應(yīng)檢查數(shù)據(jù)庫(kù)中的用戶(hù)是否存在 empty($_COOKIE['uid']) ||empty($_GET['uid']) || $_GET['uid'] != $data['id'] ? die('非法用戶(hù)') : ''; // 檢查username參數(shù) $data['username'] = empty($_GET['username']) ? die('用戶(hù)名不能為空') : $_GET['username']; // 更新數(shù)據(jù) $data['username'] = $_GET['username']; if(file_put_contents('data.json', json_encode($data))) { ??echo "用戶(hù)名已更改為{$data['username']}<br>"; } else { ??die('更新失敗'); } |
正常情況下這個(gè)頁(yè)面的鏈接是放在站點(diǎn)A下面的,比如A站點(diǎn)的csrfdemo.php頁(yè)面,用戶(hù)登錄站點(diǎn)A以后可以通過(guò)點(diǎn)擊這個(gè)鏈接來(lái)發(fā)送請(qǐng)求,比如站點(diǎn)A有一個(gè)頁(yè)面腳本,包含了這個(gè)鏈接:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php // 這里用一個(gè)data.json文件保存用戶(hù)數(shù)據(jù),模擬數(shù)據(jù)庫(kù)中的數(shù)據(jù) // 先初始化data.json中的數(shù)據(jù)為{"id":101,"username":"jack"}, 注意這句只讓它執(zhí)行一次, 然后把它注釋掉 // file_put_contents('data.json','{"id":101,"username":"jack"}'); $data = json_decode(file_get_contents('data.json'), true); // 這里為了簡(jiǎn)便, 省略了用戶(hù)身份驗(yàn)證的過(guò)程 if ($data['username']) { ??// 設(shè)置cookie ??setcookie('uid', $data['id'], 0); ??echo "登錄成功, {$data['username']}<br>"; } ?> ?<a href="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=json" rel="external nofollow" > ??更新用戶(hù)名為json ?</a> |
加載這個(gè)頁(yè)面如下:
用點(diǎn)擊頁(yè)面中的鏈接來(lái)到get-update.php頁(yè)面:
上面是正常的請(qǐng)求流程,下面來(lái)看B站點(diǎn)是如何實(shí)現(xiàn)csrf攻擊的。
2,csrf攻擊的最簡(jiǎn)單實(shí)現(xiàn)
B站點(diǎn)域名為test.yang.com,它有一個(gè)頁(yè)面csrf.php,只要用戶(hù)在維持A站點(diǎn)會(huì)話的同時(shí)打開(kāi)了這個(gè)頁(yè)面,那么B站點(diǎn)就可以實(shí)現(xiàn)csrf攻擊。至于為什么會(huì)打開(kāi)......,其實(shí)這種情景在我們?yōu)g覽網(wǎng)頁(yè)時(shí)是很常見(jiàn)的,比如我在寫(xiě)這篇博客時(shí),寫(xiě)著寫(xiě)著感覺(jué)對(duì)csrf某個(gè)地方不懂,然后就百度了,結(jié)果百度出來(lái)好多結(jié)果,假如說(shuō)有個(gè)網(wǎng)站叫csrf百科知識(shí),這個(gè)網(wǎng)站對(duì)csrf介紹的非常詳細(xì)、非常權(quán)威,那么我很可能會(huì)點(diǎn)進(jìn)去看,但是這個(gè)網(wǎng)站其實(shí)是個(gè)釣魚(yú)網(wǎng)站,它在某個(gè)訪問(wèn)頻率很高的頁(yè)面中嵌入了我博客編輯頁(yè)面的url地址,那么它就可以實(shí)現(xiàn)對(duì)我博客的csrf攻擊。好了,言歸正傳,下面來(lái)看下csrf.php腳本代碼:
?| 1 2 3 | <?php ?> <img src="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp"> |
下面再來(lái)訪問(wèn)下A站點(diǎn)的csrfdemo.php頁(yè)面:
可以看到用戶(hù)名被修改為了jsonp。
簡(jiǎn)單分析下:B站點(diǎn)的這個(gè)csrf.php利用了html中的img標(biāo)簽,我們都知道img標(biāo)簽有個(gè)src屬性,屬性值指向需要加載的圖片地址,當(dāng)頁(yè)面載入時(shí),加載圖片就相當(dāng)于向src指向的地址發(fā)起http請(qǐng)求,只要把圖片的地址修改為某個(gè)腳本地址,這樣自然就實(shí)現(xiàn)了最簡(jiǎn)單的csrf攻擊。如此說(shuō)來(lái),其實(shí)csrf很容易實(shí)現(xiàn),只不過(guò)大家都是“正人君子”,誰(shuí)沒(méi)事會(huì)閑著去做這種“下三濫”的事情。但是害人之心不可有,防人之心不可無(wú)。下面看下如何簡(jiǎn)單防范這種最簡(jiǎn)單的csrf攻擊。
3,簡(jiǎn)單防范措施
其實(shí)防范措施也比較簡(jiǎn)單,A站點(diǎn)可以在get-update.php腳本中判斷請(qǐng)求頭的來(lái)源,如果來(lái)源不是A站點(diǎn)就可以截?cái)嗾?qǐng)求,下面在get-update.php增加些代碼:?
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php // 檢查上一頁(yè)面是否為當(dāng)前站點(diǎn)下的頁(yè)面 if (!empty($_SERVER['HTTP_REFERER'])) { ??if (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) != 'html5.yang.com') { ????// 可以設(shè)置http錯(cuò)誤碼或者指向一個(gè)無(wú)害的url地址 ????//header('HTTP/1.1 404 not found'); ????//header('HTTP/1.1 403 forbiden'); ????header('Location: http://html5.yang.com/favicon.ico'); ????// 這里需要注意一定要exit(), 否則腳本會(huì)接著執(zhí)行 ????exit(); ??} ?} $str = file_get_contents('data.json'); // 代碼省略 |
但是,這樣就萬(wàn)事大吉了嗎,如果http請(qǐng)求頭被偽造了呢?A站點(diǎn)升級(jí)了防御,B站點(diǎn)同時(shí)也可以升級(jí)攻擊,通過(guò)curl請(qǐng)求來(lái)實(shí)現(xiàn)csrf,修改B站點(diǎn)的csrf.php代碼如下:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php $url = 'http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp'; $refer = 'http://html5.yang.com/'; // curl方法發(fā)起csrf攻擊 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); // 設(shè)置Referer curl_setopt($ch, CURLOPT_REFERER, $refer); // 這里需要攜帶上cookie, 因?yàn)锳站點(diǎn)get-update.php對(duì)cooke進(jìn)行了判斷 curl_setopt($ch, CURLOPT_COOKIE, 'uid=101'); curl_exec($ch); curl_close($ch); ?> <img src="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp"> |
4,小結(jié)
下面我們回到問(wèn)題的開(kāi)始,站點(diǎn)A通過(guò)cookie來(lái)跟蹤用戶(hù)會(huì)話,在cookie中存放了重要的用戶(hù)信息uid,get-update.php腳本通過(guò)判斷用戶(hù)的cookie正確與否來(lái)決定是否更改用戶(hù)信息,看來(lái)靠cookie來(lái)跟蹤會(huì)話并控制業(yè)務(wù)邏輯是不太安全的,還有最嚴(yán)重的一點(diǎn):get-update.php通過(guò)get請(qǐng)求來(lái)修改用戶(hù)信息,這個(gè)是大忌。所以站點(diǎn)A可以接著升級(jí)防御:用session來(lái)代替cookie來(lái)跟蹤用戶(hù)會(huì)話信息,將修改用戶(hù)信息的邏輯重寫(xiě),只允許用post方法來(lái)請(qǐng)求用戶(hù)信息。站點(diǎn)B同樣可以升級(jí)攻擊:curl可以構(gòu)造post請(qǐng)求,劫持session等等,不過(guò)這些我還沒(méi)研究過(guò),后續(xù)再說(shuō)吧。
轉(zhuǎn)載于:https://www.cnblogs.com/zyiii/p/8822337.html
總結(jié)
以上是生活随笔為你收集整理的PHP开发中csrf攻击的简单演示和防范的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java.lang.Incompatib
- 下一篇: 使用php蓝天采集器抓取今日头条ajax