作者 | 江南一點雨 責編 | 歐陽姝黎CSRF 就是跨域請求偽造,英文全稱是 Cross Site Request Forgery。這是一種非常常見的 Web 攻擊方式,其實是很好防禦的,但是由於經常被
2021-06-07 11:31:01
CSRF 就是跨域請求偽造,英文全稱是 Cross Site Request Forgery。 這是一種非常常見的 Web 攻擊方式,其實是很好防禦的,但是由於經常被很多開發者忽略,進而導致很多網站實際上都存在 CSRF 攻擊的安全隱患。 今天就來和大家聊一聊什麼是 CSRF 攻擊以及 CSRF 攻擊該如何防禦。 想要防禦 CSRF 攻擊,那我們得先搞清楚什麼是 CSRF 攻擊,鬆哥通過下面一張圖,來和大家梳理 CSRF 攻擊流程: 其實這個流程很簡單: CSRF 的流程大致就是這樣,接下來鬆哥用一個簡單的例子和小夥伴們展示一下 CSRF 到底是怎麼回事。 接下來,我創建一個名為 csrf-1 的 Spring Boot 項目,這個項目相當於我們上面所說的網上銀行網站,創建項目時引入 Web 和 Spring Security 依賴,如下: 創建成功後,方便起見,我們直接將 Spring Security 使用者名/密碼 配置在 application.properties 檔案中: 然後我們提供兩個測試介面: 假設 /transfer 是一個轉賬介面(這裡是假設,主要是給大家演示 CSRF 攻擊,真實的轉賬介面比這複雜)。 最後我們還需要配置一下 Spring Security,因為 Spring Security 中預設是可以自動防禦 CSRF 攻擊的,所以我們要把這個關閉掉: 配置完成後,我們啟動 csrf-1 項目。 接下來,我們再創建一個 csrf-2 項目,這個項目相當於是一個危險網站,為了方便,這裡創建時我們只需要引入 web 依賴即可。 項目創建成功後,首先修改項目埠: 然後我們在 resources/static 目錄下創建一個 hello.html ,內容如下: 這裡有一個超連結,超連結的文字是點選檢視美女圖片,當你點選了超連結之後,會自動請求 http://localhost:8080/transfer 介面,同時隱藏域還攜帶了兩個參數。 配置完成後,就可以啟動 csrf-2 項目了。 接下來,使用者首先訪問 csrf-1 項目中的介面,在訪問的時候需要登入,使用者就執行了登入操作,訪問完整後,使用者並沒有執行登出操作,然後使用者訪問 csrf-2 中的頁面,看到了超連結,好奇這美女到底長啥樣,一點選,結果錢就被人轉走了。 先來說說防禦思路。 CSRF 防禦,一個核心思路就是在前端請求中,新增一個隨機數。 因為在 CSRF 攻擊中,黑客網站其實是不知道使用者的 Cookie 具體是什麼的,他是讓使用者自己傳送請求到網上銀行這個網站的,因為這個過程會自動攜帶上 Cookie 中的資訊。 所以我們的防禦思路是這樣:使用者在訪問網上銀行時,除了攜帶 Cookie 中的資訊之外,還需要攜帶一個隨機數,如果使用者沒有攜帶這個隨機數,則網上銀行網站會拒絕該請求。黑客網站誘導使用者點選超連結時,會自動攜帶上 Cookie 中的資訊,但是卻不會自動攜帶隨機數,這樣就成功的避免掉 CSRF 攻擊了。 Spring Security 中對此提供了很好的支援,我們一起來看下。 Spring Security 中預設實際上就提供了 csrf 防禦,但是需要開發者做的事情比較多。 首先我們來創建一個新的 Spring Boot 工程,創建時引入 Spring Security、Thymeleaf 和 web 依賴。 項目創建成功後,我們還是在 application.properties 中配置使用者名/密碼: 接下來,我們提供一個測試介面: 注意,這個測試介面是一個 POST 請求,因為預設情況下,GET、HEAD、TRACE 以及 OPTIONS 是不需要驗證 CSRF 攻擊的。 然後,我們在 resources/templates 目錄下,新建一個 thymeleaf 模版,如下: 注意,在傳送 POST 請求的時候,還額外攜帶了一個隱藏域,隱藏域的 key 是 ${_csrf.parameterName},value 則是 ${_csrf.token}。 這兩個值服務端會自動帶過來,我們只需要在前端渲染出來即可。 接下來給前端 hello.html 頁面新增一個控制器,如下: 新增完成後,啟動項目,我們訪問 hello 頁面,在訪問時候,需要先登入,登入成功之後,我們可以看到登入請求中也多了一個參數,如下: 可以看到,這裡也多了 _csrf 參數。 這裡我們用了 Spring Security 的預設登入頁面,如果大家使用自定義登入頁面,可以參考上面 hello.html 的寫法,通過一個隱藏域傳遞 _csrf 參數。 訪問到 hello 頁面之後,再去點選按鈕,就可以訪問到 hello 介面了。 小夥伴們可以自行嘗試在 hello.html 頁面中,去掉 _csrf 參數,看看訪問 hello 介面的效果。 這是 Spring Security 中預設的方案,通過 Model 將相關的資料帶到前端來。 如果你的項目是前後端不分項目,這種方案就可以了,如果你的項目是前後端分離項目,這種方案很明顯不夠用。 如果是前後端分離項目,Spring Security 也提供瞭解決方案。 這次不是將 _csrf 放在 Model 中返回前端了,而是放在 Cookie 中返回前端,配置方式如下: 有小夥伴可能會說放在 Cookie 中不是又被黑客網站盜用了嗎?其實不會的,大家注意如下兩個問題: 理解透了上面兩點,你就會發現 _csrf 放在 Cookie 中是沒有問題的,但是大家注意,配置的時候我們通過 withHttpOnlyFalse 方法獲取了 CookieCsrfTokenRepository 的例項,該方法會設定 Cookie 中的 HttpOnly 屬性為 false,也就是允許前端通過 js 操作 Cookie(否則你就沒有辦法獲取到 _csrf)。 配置完成後,重啟項目,此時我們就發現返回的 Cookie 中多了一項: 接下來,我們通過自定義登入頁面,來看看前端要如何操作。 首先我們在 resources/static 目錄下新建一個 html 頁面叫做 login.html: 這段 html 我給大家解釋下: 服務端我們也稍作修改,如下: 一方面這裡給 js 檔案放行。 另一方面配置一下登入頁面,以及登入成功的回撥,這裡簡單期間,登入成功的回撥我就給一個字元串就可以了。大家感興趣的話,可以檢視本系列前面文章,有登入成功後回撥的詳細解釋。 OK,所有事情做完之後,我們訪問 login.html 頁面,輸入使用者名密碼進行登入,結果如下: 可以看到,我們的 _csrf 配置已經生效了。 小夥伴們可以自行嘗試從登入參數中去掉 _csrf,然後再看看效果。 好了,今天主要和小夥伴們介紹了 csrf 攻擊以及如何防禦的問題。大家看到,csrf 攻擊主要是藉助了瀏覽器預設傳送 Cookie 的這一機制,所以如果你的前端是 App、小程式之類的應用,不涉及瀏覽器應用的話,其實可以忽略這個問題,如果你的前端包含瀏覽器應用的話,這個問題就要認真考慮了。 好了 ,本文就說到這裡,本文相關案例我已經上傳到 GitHub ,大家可以自行下載:https://github.com/lenve/spring-security-samplesCSRF原理
CSRF實踐
spring.security.user.name=javaboy
spring.security.user.password=123@RestController
public class HelloController {
@PostMapping("/transfer")
public void transferMoney(String name, Integer money) {
System.out.println("name = " + name);
System.out.println("money = " + money);
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
}@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf()
.disable();
}
}server.port=8081
<body>
<form action="http://localhost:8080/transfer" method="post">
<input type="hidden" value="javaboy" name="name">
<input type="hidden" value="10000" name="money">
<input type="submit" value="點選檢視美女圖片">
</form>
</body>CSRF防禦
3.1 預設方案
spring.security.user.name=javaboy
spring.security.user.password=123@Controller
public class HelloController {
@PostMapping("/hello")
@ResponseBody
public String hello() {
return "hello";
}
}<body>
<form action="/hello" method="post">
<input type="hidden" th:value="${_csrf.token}" th:name="${_csrf.parameterName}">
<input type="submit" value="hello">
</form>
</body>@GetMapping("/hello")
public String hello2() {
return "hello";
}3.2 前後端分離方案
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery.min.js"></script>
<script src="js/jquery.cookie.js"></script>
</head>
<body>
<div>
<input type="text" id="username">
<input type="password" id="password">
<input type="button" value="登入" id="loginBtn">
</div>
<script>
$("#loginBtn").click(function () {
let _csrf = $.cookie('XSRF-TOKEN');
$.post('/login.html',{username:$("#username").val(),password:$("#password").val(),_csrf:_csrf},function (data) {
alert(data);
})
})
</script>
</body>
</html>@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.successHandler((req,resp,authentication)->{
resp.getWriter().write("success");
})
.permitAll()
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}小結
相關文章
作者 | 江南一點雨 責編 | 歐陽姝黎CSRF 就是跨域請求偽造,英文全稱是 Cross Site Request Forgery。這是一種非常常見的 Web 攻擊方式,其實是很好防禦的,但是由於經常被
2021-06-07 11:31:01
感謝您的閱讀!我們知道一款手機的電池續航一定是由多種方面共同作用的結果,手機的電池續航能力,它包括手機的電池容量,手機充電時間、手機的系統表現、以及手機本身在演算法以及
2021-06-07 11:30:18
最近創維新推出了一款全面屏電視「創維G750Pro」,採用4K解析度液晶屏,具有光學防藍光護眼,搭載4陣列麥克風、高進光AI攝像頭,具有遠場語音AI互動、AIOT家電互聯、視訊通話等功能
2021-06-07 11:28:48
No.1由於一些中小企業生產特點,發貨記錄常常很多,那麼對於這樣的商品進行管理,就是一個複雜的問題。如果使用大型管理軟體,顯然有些浪費,如果用簡單的Excel來管理,又顯得麻煩。本
2021-06-07 11:28:19
【CNMO新聞】日前,萬物新生(愛回收)集團向美國SEC遞交招股書,準備在紐交所上市。此次赴美上市的募集資金,將主要用於進一步提升技術能力,提供更多元化的平臺服務,拓展愛回收門店網
2021-06-07 11:28:07
今天我們來聊一款價效比很高千元5G手機,雖然是千元價格,但是卻擁有旗艦的配置,因此筆者特別希望大家能夠發現這樣的好貨,用上更好的產品,今天要聊的這款手機是OPPO子品牌realme的
2021-06-07 11:27:13