CSRF(Cross-site request forgery),即跨站請求僞造,本質就是攻擊者僞造你的身份發送請求。
攻擊流程
在 Google 上找了一張流程圖:
- 用戶訪正常登錄訪問 mybank.com 網站,登錄成功後,mybank 服務端會產生 Cookie 信息給瀏覽器;
- 在用戶不登出 mybank.com 的情況下訪問攻擊網站 attacker.com 的攻擊鏈接,此鏈接會直接訪問 mybank.com,此時會攜帶着 mybank 的 Cookie,從而執行惡意腳本;
首先這裏不能只侷限於 Cookie,比如 Token,也是可以通過抓包分析獲取的。也就是說 CSRF 的本質就是攻擊者僞造你的身份發送請求,那麼身份用什麼體現呢,就是 Cookie 或者 Token。
示例
先看一個簡單的模擬例子:
用戶登錄頁面:
<form action="/login" method="post" enctype="multipart/form-data">
<p> 用戶名:<input type="text" name="username">
<p> 密碼:<input type="text" name="password">
<input type="submit" value="提交">
</form>
後臺代碼:
@RequestMapping("login")
public Object login(@RequestParam("username")String username, @RequestParam("password")String password, HttpServletResponse response){
Cookie cookie = new Cookie("8080Cookie", "123456");
cookie.setPath("/");
response.addCookie(cookie);
return "OK";
}
用戶登錄成功後這裏會生成一個 Cookie:
攻擊頁面:
<h2>attack</h2>
<form action="http://localhost:8080/pay" method="post" enctype="multipart/form-data">
<input type="text" name="money" value = "1000" style="visibility: hidden;">
<input type="submit" value="恭喜你中獎了">
</form>
這裏可以做成比如超鏈接等,如果用戶點擊了這個按鈕,那麼實際會攜帶着用戶的 Cookie 信息去訪問之前的系統,即“僞造你的身份發送請求”:
後端代碼:
@RequestMapping("pay")
public Object pay(@RequestParam("money")Integer money, HttpServletRequest request){
System.out.printf("獲取了 Cookie:%s",
Stream.of(request.getCookies()).collect(Collectors.toMap(Cookie::getName, Cookie::getValue)).get("8080Cookie"));
return String.format("扣掉了 %d 元錢",money);
}
這樣會出現跨站請求成功:
如果無法獲取 Cookie 的話,後端代碼會拋出 NPE 異常。
防禦方式
總得來說沒有絕對的防禦方式,其實將破解過程設置的越複雜,也算是一種成功的防禦。
通過 HTTP 請求頭中的 Referer 和 Origin 屬性
這兩種方式其實並不算可靠,這裏借用一篇博客中的描述(相關鏈接在文末):
- referer屬性
記錄了該http請求的來源地址,但有些場景不適合將來源URL暴露給服務器,所以可以設置不用上傳,並且referer屬性是可以修改的,所以在服務器端校驗referer屬性並沒有那麼可靠
- origin屬性
通過XMLHttpRequest、Fetch發起的跨站請求或者Post方法發送請求時,都會帶上origin,所以服務器可以優先判斷Origin屬性,再根據實際情況判斷是否使用referer判斷。
CSRF Token
這種應該是常用的一種方式,就是瀏覽器向服務器發送請求後,服務器端生成一個隨機的 Token,然請求的時候就攜帶這個 Token,服務器端再進行校驗,關於這個可以參看這兩篇博客:
- https://blog.csdn.net/Dongguabai/article/details/81150989
- https://blog.csdn.net/Dongguabai/article/details/81151035
設置 SameSite 屬性
Chrome 51 開始,瀏覽器的 Cookie 新增加了一個 SameSite
屬性,用來防止 CSRF 攻擊和用戶追蹤。
關於 SameSite 屬性,可以參看:
- http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html
但是在動靜分離的情況下,這時候靜態資源會單獨部署,這時候 SameSite 屬性限制比較大。所以最常見的還是基於 Token 的方式去處理。