首頁  >  文章  >  後端開發  >  PHP開發中csrf攻擊的示範與防範詳解

PHP開發中csrf攻擊的示範與防範詳解

墨辰丷
墨辰丷原創
2018-05-22 16:02:131349瀏覽

CSRF的全名為Cross-site request forgery,它的中文名為跨站請求偽造(偽造跨站請求【這樣讀順口一點】)CSRF是一種夾持用戶在已經登陸的web應用程式上執行非本意的操作的攻擊方式。相較於XSS,CSRF是利用了系統對頁面瀏覽器的信任,XSS則利用了系統對使用者的信任。

csrf攻擊,即cross site request forgery跨站(域名)請求偽造,這裡的forgery就是偽造的意思。網路上有很多關於csrf的介紹,例如一位前輩的文章CSRF的攻擊方式詳解,參考這篇文章簡單解釋下:csrf 攻擊能夠實現依賴於這樣一個簡單的事實:我們在用瀏覽器瀏覽網頁時通常會開啟好幾個瀏覽器標籤(或視窗),如果我們登入了一個網站A,網站A如果是透過cookie來追蹤使用者的會話,那麼在使用者登入了網站A之後,網站A就會在使用者的客戶端設置cookie,假如網站A有一個頁面siteA-page.php(url資源)被網站B知道了url位址,而這個頁面的位址以某種方式被嵌入到了B網站的一個頁面siteB-page.php中,如果這時使用者在維持A網站會話的同時開啟了B網站的siteB-page.php,那麼只要siteB-page.php頁面可以觸發這個url位址(請求A網站的url資源)就實現了csrf攻擊。

上面的解釋很拗口,下面舉個簡單的例子來示範下。

1,背景和正常的請求流程

A網站網域為html5.yang.com,它有一個/get-update.php?uid=uid&username=username位址,可以看到這個位址可以透過get方法傳遞一些參數,假如這個頁面的邏輯是:它透過判斷uid是否合法來更新username,這個頁面腳本如下:


# #

<?php
// 这里简便起见, 从data.json中取出数据代替请求数据库
$str = file_get_contents(&#39;data.json&#39;);
$data = json_decode($str, true);

// 检查cookie和请求更改的uid, 实际应检查数据库中的用户是否存在
empty($_COOKIE[&#39;uid&#39;]) ||empty($_GET[&#39;uid&#39;]) || $_GET[&#39;uid&#39;] != $data[&#39;id&#39;] ? die(&#39;非法用户&#39;) : &#39;&#39;;
// 检查username参数
$data[&#39;username&#39;] = empty($_GET[&#39;username&#39;]) ? die(&#39;用户名不能为空&#39;) : $_GET[&#39;username&#39;];

// 更新数据
$data[&#39;username&#39;] = $_GET[&#39;username&#39;];
if(file_put_contents(&#39;data.json&#39;, json_encode($data))) {
  echo "用户名已更改为{$data[&#39;username&#39;]}<br>";
} else {
  die(&#39;更新失败&#39;);
}

正常情況下這個頁面的連結是放在網站A下面的,例如A網站的csrfdemo.php頁面,使用者登入網站A以後可以透過點擊這個連結來傳送請求,例如網站A有一個頁面腳本,包含了這個連結:

<?php
// 这里用一个data.json文件保存用户数据,模拟数据库中的数据
// 先初始化data.json中的数据为{"id":101,"username":"jack"}, 注意这句只让它执行一次, 然后把它注释掉
// file_put_contents(&#39;data.json&#39;,&#39;{"id":101,"username":"jack"}&#39;);

$data = json_decode(file_get_contents(&#39;data.json&#39;), true);

// 这里为了简便, 省略了用户身份验证的过程
if ($data[&#39;username&#39;]) {
  // 设置cookie
  setcookie(&#39;uid&#39;, $data[&#39;id&#39;], 0);
  echo "登录成功, {$data[&#39;username&#39;]}<br>";
}
?>

 <a href="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=json" rel="external nofollow" >
  更新用户名为json
 </a>

#載入這個頁面如下:

# #用點擊頁面中的連結來到get-update.php頁面:

#上面是正常的請求流程,下面來看B站點是如何實作csrf攻擊的。

2,csrf攻擊的最簡單實作

B網站網域為test.yang.com,它有一個頁面csrf.php,只要用戶在維持A網站會話的同時打開了這個頁面,那麼B站點就可以實現csrf攻擊。至於為什麼會打開......,其實這種情景在我們瀏覽網頁時是很常見的,比如我在寫這篇博客時,寫著寫著感覺對csrf某個地方不懂,然後就百度了,結果百度出來好多結果,假如說有個網站叫csrf百科知識,這個網站對csrf介紹的非常詳細、非常權威,那麼我很可能會點進去看,但是這個網站其實是個釣魚網站,它在某在瀏覽頻率很高的頁面中嵌入了我部落格編輯頁面的url位址,那麼它就可以實現對我部落格的csrf攻擊。好了,言歸正傳,下面來看下csrf.php腳本程式碼:

<?php
?>
<img src="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp">

可以看到上面的程式碼沒有php程式碼,只有一個img標籤,img標籤的src就是A站點的那個更新用戶名的鏈接,只不過把username改為了jsonp,訪問站點B的csrf.php這個頁面:

下面再來訪問下A網站的csrfdemo.php頁面:

可以看到使用者名稱被修改為了jsonp。

簡單分析下:B網站的這個csrf.php利用了html中的img標籤,我們都知道img標籤有個src屬性,屬性值指向需要載入的圖片位址,當頁面載入時,載入圖片就相當於向src指向的位址發動http請求,只要把圖片的位址修改為某個腳本位址,這樣自然就實現了最簡單的csrf攻擊。如此說來,其實csrf很容易實現,只不過大家都是“正人君子”,誰沒事會閒著去做這種“下三濫”的事情。但是害人之心不可有,防人之心不可無。下面看如何簡單防範這種最簡單的csrf攻擊。

3,簡單防範措施

其實防範措施也比較簡單,A網站可以在get-update.php腳本中判斷請求頭的來源,如果來源不是A網站就可以截斷請求,以下在get-update.php增加些程式碼: 

<?php
// 检查上一页面是否为当前站点下的页面
if (!empty($_SERVER[&#39;HTTP_REFERER&#39;])) {
  if (parse_url($_SERVER[&#39;HTTP_REFERER&#39;], PHP_URL_HOST) != &#39;html5.yang.com&#39;) {
    // 可以设置http错误码或者指向一个无害的url地址
    //header(&#39;HTTP/1.1 404 not found&#39;);
    //header(&#39;HTTP/1.1 403 forbiden&#39;);
    header(&#39;Location: http://html5.yang.com/favicon.ico&#39;);
    // 这里需要注意一定要exit(), 否则脚本会接着执行
    exit();
  }
 }

$str = file_get_contents(&#39;data.json&#39;);
// 代码省略

但是,这样就万事大吉了吗,如果http请求头被伪造了呢?A站点升级了防御,B站点同时也可以升级攻击,通过curl请求来实现csrf,修改B站点的csrf.php代码如下:

<?php
$url = &#39;http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp&#39;;
$refer = &#39;http://html5.yang.com/&#39;;
// curl方法发起csrf攻击
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
// 设置Referer
curl_setopt($ch, CURLOPT_REFERER, $refer);
// 这里需要携带上cookie, 因为A站点get-update.php对cooke进行了判断
curl_setopt($ch, CURLOPT_COOKIE, &#39;uid=101&#39;);
curl_exec($ch);
curl_close($ch);
?>
<img src="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp">

这样同样可以实现csrf攻击的目的。那么就没有比较好的防范方法了吗?

4,小结

下面我们回到问题的开始,站点A通过cookie来跟踪用户会话,在cookie中存放了重要的用户信息uid,get-update.php脚本通过判断用户的cookie正确与否来决定是否更改用户信息,看来靠cookie来跟踪会话并控制业务逻辑是不太安全的,还有最严重的一点:get-update.php通过get请求来修改用户信息,这个是大忌。所以站点A可以接着升级防御:用session来代替cookie来跟踪用户会话信息,将修改用户信息的逻辑重写,只允许用post方法来请求用户信息。站点B同样可以升级攻击:curl可以构造post请求,劫持session等等,不过这些我还没研究过,后续再说吧。

相关推荐:

php curl带有csrf-token验证模拟提交实例详解

php表单防止重复提交(防csrf漏洞) 

php curl带有csrf-token验证模拟提交方法

以上是PHP開發中csrf攻擊的示範與防範詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn