什么是CSRF?
你可以理解為:攻擊者盜用你的身份,以你的名義發送惡意請求,它被稱為“跨站請求偽造”,它能干的事情有很多:當你打開某個網站時,你另一個已經打開的購物網站已經完成支付;以你名義發送郵件,發評論;網路蠕蟲;虛擬貨幣轉賬…
CSRF攻擊流程
- 用戶登錄A網站(受信任的)
- A網站確認身份
- 在A中訪問B網站鏈接(危險的)
- B網站拿到A中的 cookie 后直接向其服務器發送請求
這看似簡單,其中卻有一些值得注意的問題,比如:
1. B網站向A服務器發送請求,是否會引起跨域問題?
不會,因為并不是所有的請求都會引起跨域,如 HTML 中的 <img> 和 <script> 標簽就不會,而且它們的 src 中放的鏈接其實就是一次 get請求,
著名的jsonp就是用了 script 允許跨域的特性實作的
而且,通過 form 表單發送POST請求的方式也會被用來做攻擊,筆者曾看過這樣的一段代碼:
<? php
session_start();
if(isset($_POST['toBankId']) && isset($_POST['money'])){
buy_stocks($_POST['toBankId'],$_POST['money']);
}
?>
# 服務端為了防止由img導致的get請求輕易地獲取資料,改為只接收POST請求,但
<body onload="steal()">
<iframe name="steal" display="none">
<form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php" target="steal">
<input type="hidden" name="toBankId" value="11" />
<input type="hidden" name="money" value="1000" />
</form>
</iframe>
<script>
function steal(){
iframe=document.frames["steal"];
iframe.document.submit("transfer");
}
</script>
</body>
隱藏的 iframe 表單提交 —— 如果一個 form 表單的 target 值等于 iframe 的 name 值,那么這個表單資料會在 iframe 中提交,進而跳轉,此時,如果這個 iframe 還設定了 display:none; 那么在頁面看來并沒有什么“不適”,
2. 攻擊者能拿到cookie嗎?
不能,CSRF攻擊是攻擊者利用 cookie 欺騙服務器,但攻擊者并不能知道 cookie 的內容,而且,對于服務器回傳的資料(結果),由于瀏覽器同源的限制,攻擊者也無法決議,他所做的也就是給服務器發送請求,以執行請求中所描述的命令,在服務器端直接修改資料的值,而非竊取資料!
3. 上面說要csrf生效,基本需滿足兩點:1、登錄受信任網站A,并在本地生成Cookie;2、在不登出A的情況下,訪問危險網站B,那如果我不滿足以上兩個條件中的一個,我就不會受到CSRF的攻擊?
理論上確實如此,但你不能保證以下情況不會發生:
- 你不能保證你登錄了一個網站后,不再打開一個tab頁面并訪問另外的網站,
- 你不能保證你關閉瀏覽器了后,你本地的Cookie立刻過期,你上次的會話已經結束,(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認為關閉瀏覽器就等于退出登錄/結束會話了…)
- 所謂的攻擊網站,可能是一個存在其他漏洞的可信任的經常被人訪問的網站,
CSRF的防御
1. 前端token,后端校驗
CSRF 攻擊之所以能夠成功,是因為攻擊者可以偽造用戶的請求,該請求中所有的用戶驗證資訊都是存在于 cookie 中,因此攻擊者可以在不知道這些驗證資訊的情況下直接利用用戶自己的 cookie 來通過安全驗證,
要抵御 CSRF,關鍵在于在請求中放入攻擊者所不能偽造或者拿不到的資訊;或者可以在 HTTP 請求中以引數的形式加入一個隨機產生的 token,并將token保存到本地(cookie中),然后在服務器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 內容不正確,則認為可能是 CSRF 攻擊而拒絕該請求,
簡單來說就是:在服務端生成一個隨機的token并保存起來并且跟隨請求帶給前端,而前端拿到后保存到一個隱藏域中,跟隨每次請求帶到后端:
// 后端代碼,這里以node(koa)為例
let csrfToken=parseInt(Math.random()*9999999,10);
ctx.cookies.set('csrfToken',csrfToken);
<input calss="csrfToken" type="hidden" name="csrfToken" value="" />
<script>
document.querySelector(".csrfToken").value=getMyCookie('csrfToken');
function getMyCookie(key){
var val = "";
// 對cookie操作
var cookies = document.cookie;
cookies = cookies.replace(/\s/,"");
var cookie_array = cookies.split(";");
for(i=0;i<cookie_array.length;i++){
// name=mxc
var cookie = cookie_array[i];
var array = cookie.split("=");
if(array[0]==key){
val = array[1];
}
}
return val;
}
</script>
在每次請求的回呼中都生成一個新的值,然后在下一次請求過來時校驗(cookie中的值和引數中的值相同,并且和后端生成的值也相等),
這樣就要求必須同時知道引數中的token和cookie中的token,才能成功發動攻擊
2. same-site屬性
cookie自帶的有一個sami-site屬性,可以控制第三方網站是否攜帶cookies:
- Strict:任何請求都不允許帶cookies
- Lax(默認):允許比如鏈接可以攜帶cookies
- None
寫法:
// 以koa為例
ctx.cookies.set(
'cid', //第一個引數,key
'hello world', //第二個引數,value
{ //第三個引數,cookie設定
domain: 'localhost', // 寫cookie所在的域名
path: '/index', // 寫cookie所在的路徑
maxAge: 10 * 60 * 1000, // cookie有效時長(http1.1)
expires: new Date('2017-02-15'), // cookie失效時間(http1.0)
httpOnly: false, // 是否只用于http請求中獲取
sameSite: Strict,
overwrite: false // 是否允許重寫
}
)
但是這種方式目前還存在兼容性問題,而且受支持程度并不好,
3. 訪問來源
其實簡單的還有一種方式:通過 document.referrer 判斷當前頁面是從哪個鏈接過來的,通常也會隨header攜帶到服務端,服務端也可以獲取到:req.headers.referrer
但是referrer可以被偽造,這種方式非常不安全,
當然,還可以通過如Spring中的某些設定或者一些方式去達到防御的目的,筆者在查閱資料途中發現了一篇文章,手段比較偏后端的,感興趣的可以點我移步這里查看,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/280230.html
標籤:其他
上一篇:前端js實作一鍵復制功能
