欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot 防御 CSRF 攻擊的流程及原理解析

 更新時(shí)間:2023年05月03日 10:10:25   作者:mry6  
CSRF是一種非常常見(jiàn)的Web攻擊方式,其實(shí)是很好防御的,但是由于經(jīng)常被很多開(kāi)發(fā)者忽略,進(jìn)而導(dǎo)致很多網(wǎng)站實(shí)際上都存在 CSRF 攻擊的安全隱患,這篇文章主要介紹了SpringBoot 如何防御 CSRF 攻擊,需要的朋友可以參考下

CSRF 就是跨域請(qǐng)求偽造,英文全稱(chēng)是 Cross Site Request Forgery。
這是一種非常常見(jiàn)的Web攻擊方式,其實(shí)是很好防御的,但是由于經(jīng)常被很多開(kāi)發(fā)者忽略,進(jìn)而導(dǎo)致很多網(wǎng)站實(shí)際上都存在 CSRF 攻擊的安全隱患。

今天和大家聊一聊什么是 CSRF 攻擊以及 CSRF 攻擊該如何防御。

CSRF 原理

想要防御 CSRF 攻擊,那我們需要先搞清楚什么是 CSRF 攻擊,通過(guò)下面圖例來(lái)和大家梳理 CSRF 攻擊流程:

其實(shí)這個(gè)流程很簡(jiǎn)單:
1.假設(shè)用戶(hù)打開(kāi)了招商網(wǎng)上銀行網(wǎng)站,并且登錄。
2.登錄成功后,網(wǎng)上銀行會(huì)返回Cookie給前端,瀏覽器將Cookie保存下來(lái)。
3.用戶(hù)在沒(méi)有登出網(wǎng)上銀行的情況下,在瀏覽器里打開(kāi)了一個(gè)新的選項(xiàng)卡,然后又去訪(fǎng)問(wèn)了一個(gè)危險(xiǎn)網(wǎng)站。
4.這個(gè)危險(xiǎn)網(wǎng)站上有一個(gè)超鏈接,超鏈接的地址指向了招商網(wǎng)上銀行。
4.用戶(hù)點(diǎn)擊了這個(gè)鏈接,由于這個(gè)超鏈接會(huì)自動(dòng)攜帶上瀏覽器中保存的Cookie,所以用戶(hù)不知不覺(jué)中就訪(fǎng)問(wèn)了網(wǎng)上銀行,進(jìn)而可能給自己造成了損失。

CSRF的流程大致就是這樣,接下來(lái)用一個(gè)簡(jiǎn)單的例子展示一下CSRF到底是怎么一回事。

CSRF實(shí)踐

1.我創(chuàng)建一個(gè)名為 csrf-mry 的 Spring Boot 項(xiàng)目,這個(gè)項(xiàng)目相當(dāng)于我們上面所說(shuō)的網(wǎng)上銀行網(wǎng)站,創(chuàng)建項(xiàng)目時(shí)引入 Web 和 Spring Security依賴(lài),如下:

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>

2.創(chuàng)建成功后,方便起見(jiàn),我們直接將 Spring Security 用戶(hù)名/密碼 配置在 application.properties 文件中:

server.port= 8866
spring.security.user.name=javaboy
spring.security.user.password=123

3.然后我們提供兩個(gè)測(cè)試接口

package com.mry.csrf.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CsrfDemoController {
    @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";
    }
}

假設(shè) /transfer 是一個(gè)轉(zhuǎn)賬接口(這里是假設(shè),主要是給大家演示 CSRF 攻擊,真實(shí)的轉(zhuǎn)賬接口比這復(fù)雜)。

4.我們還需要配置一下 Spring Security,因?yàn)?Spring Security 中默認(rèn)是可以自動(dòng)防御 CSRF 攻擊的,所以我們要把這個(gè)關(guān)閉掉。

package com.mry.csrf.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf()
                .disable();
    }
}

配置完成后,我們啟動(dòng) csrf-simulate-web 項(xiàng)目。

5.我們?cè)賱?chuàng)建一個(gè) csrf-loophole-web 項(xiàng)目,這個(gè)項(xiàng)目相當(dāng)于是一個(gè)危險(xiǎn)網(wǎng)站,為了方便,這里創(chuàng)建時(shí)我們只需要引入 web 依賴(lài)即可。
項(xiàng)目創(chuàng)建成功后,首先修改項(xiàng)目端口:

server.port= 8855

6.然后我們?cè)?resources/static 目錄下創(chuàng)建一個(gè) hello.html ,內(nèi)容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8866/transfer" method="post">
        <input type="hidden" value="javaboy" name="name">
        <input type="hidden" value="10000" name="money">
        <input type="submit" value="點(diǎn)擊查看美女圖片">
    </form>
</body>
</html>

這里有一個(gè)超鏈接,超鏈接的文本是點(diǎn)擊查看美女圖片,當(dāng)你點(diǎn)擊了超鏈接之后,會(huì)自動(dòng)請(qǐng)求 http://localhost:8866/transfer 接口,同時(shí)隱藏域還攜帶了兩個(gè)參數(shù)。

配置完成后,就可以啟動(dòng) csrf-loophole-web 項(xiàng)目了。

接下來(lái),用戶(hù)首先訪(fǎng)問(wèn) csrf-simulate-web 項(xiàng)目中的接口,在訪(fǎng)問(wèn)的時(shí)候需要登錄,用戶(hù)就執(zhí)行了登錄操作,訪(fǎng)問(wèn)完整后,用戶(hù)并沒(méi)有執(zhí)行登出操作,然后用戶(hù)訪(fǎng)問(wèn) csrf-loophole-web 中的頁(yè)面,看到了超鏈接,好奇這美女到底長(zhǎng)啥樣,一點(diǎn)擊,結(jié)果錢(qián)就被人轉(zhuǎn)走了。

CSRF防御

先來(lái)說(shuō)說(shuō)防御思路。

CSRF 防御,一個(gè)核心思路就是在前端請(qǐng)求中,添加一個(gè)隨機(jī)數(shù)。

因?yàn)樵?CSRF 攻擊中,黑客網(wǎng)站其實(shí)是不知道用戶(hù)的 Cookie 具體是什么的,他是讓用戶(hù)自己發(fā)送請(qǐng)求到網(wǎng)上銀行這個(gè)網(wǎng)站的,因?yàn)檫@個(gè)過(guò)程會(huì)自動(dòng)攜帶上 Cookie 中的信息。

所以我們的防御思路是這樣:用戶(hù)在訪(fǎng)問(wèn)網(wǎng)上銀行時(shí),除了攜帶 Cookie 中的信息之外,還需要攜帶一個(gè)隨機(jī)數(shù),如果用戶(hù)沒(méi)有攜帶這個(gè)隨機(jī)數(shù),則網(wǎng)上銀行網(wǎng)站會(huì)拒絕該請(qǐng)求。黑客網(wǎng)站誘導(dǎo)用戶(hù)點(diǎn)擊超鏈接時(shí),會(huì)自動(dòng)攜帶上 Cookie 中的信息,但是卻不會(huì)自動(dòng)攜帶隨機(jī)數(shù),這樣就成功的避免掉 CSRF 攻擊了。

Spring Security 中對(duì)此提供了很好的支持,我們一起來(lái)看下。

前后端不分離方案

Spring Security 中默認(rèn)實(shí)際上就提供了 csrf 防御,但是需要開(kāi)發(fā)者做的事情比較多。
首先我們來(lái)創(chuàng)建一個(gè)新的 Spring Boot 工程,創(chuàng)建時(shí)引入 Spring Security、Thymeleaf 和 web 依賴(lài)。

1.pom信息

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-thymeleaf</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

2.項(xiàng)目創(chuàng)建成功后,我們還是在 application.properties 中配置用戶(hù)名/密碼

spring.security.user.name=mry
spring.security.user.password=123456

3.接下來(lái),我們提供一個(gè)測(cè)試接口

package com.mry.csrf.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SecurityCsrfController {
    @PostMapping("/hello")
    @ResponseBody
    public String hello() {
        return "hello";
    }
}

注意,這個(gè)測(cè)試接口是一個(gè) POST 請(qǐng)求,因?yàn)槟J(rèn)情況下,GET、HEAD、TRACE 以及 OPTIONS 是不需要驗(yàn)證 CSRF 攻擊的。

4.然后,我們?cè)?resources/templates 目錄下,新建一個(gè) thymeleaf 模版

<!DOCTYPE html>
<!--導(dǎo)入thymeleaf的名稱(chēng)空間-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/hello" method="post">
        <input type="hidden" th:value="${_csrf.token}" th:name="${_csrf.parameterName}">
        <input type="submit" value="hello">
    </form>
</body>
</html>

注意,在發(fā)送 POST 請(qǐng)求的時(shí)候,還額外攜帶了一個(gè)隱藏域,隱藏域的 key 是 ${_csrf.parameterName},value 則是 ${_csrf.token}。

這兩個(gè)值服務(wù)端會(huì)自動(dòng)帶過(guò)來(lái),我們只需要在前端渲染出來(lái)即可。

5.接下來(lái)給前端 hello.html 頁(yè)面添加一個(gè)控制器

@GetMapping("/hello")
public String hello2() {
	return "hello";
}

6.添加完成后,啟動(dòng)項(xiàng)目,我們?cè)L問(wèn) hello 頁(yè)面,在訪(fǎng)問(wèn)時(shí)候,需要先登錄,登錄成功之后,我們可以看到登錄請(qǐng)求中也多了一個(gè)參數(shù)

這里我們用了 Spring Security 的默認(rèn)登錄頁(yè)面,如果大家使用自定義登錄頁(yè)面,可以參考上面 hello.html 的寫(xiě)法,通過(guò)一個(gè)隱藏域傳遞 _csrf 參數(shù)。

訪(fǎng)問(wèn)到 hello 頁(yè)面之后,再去點(diǎn)擊【hello】按鈕,就可以訪(fǎng)問(wèn)到 hello 接口了。

這是 Spring Security 中默認(rèn)的方案,通過(guò) Model 將相關(guān)的數(shù)據(jù)帶到前端來(lái)。

如果你的項(xiàng)目是前后端不分項(xiàng)目,這種方案就可以了,如果你的項(xiàng)目是前后端分離項(xiàng)目,這種方案很明顯不夠用。

前后端分離方案

如果是前后端分離項(xiàng)目,Spring Security 也提供了解決方案。
這次不是將 _csrf 放在 Model 中返回前端了,而是放在 Cookie 中返回前端,配置方式如下:

package com.mry.csrf.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@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());
    }
}

有小伙伴可能會(huì)說(shuō)放在 Cookie 中不是又被黑客網(wǎng)站盜用了嗎?其實(shí)不會(huì)的,大家注意如下兩個(gè)問(wèn)題:
(1)黑客網(wǎng)站根本不知道你的 Cookie 里邊存的啥,他也不需要知道,因?yàn)?CSRF 攻擊是瀏覽器自動(dòng)攜帶上 Cookie 中的數(shù)據(jù)的。
(2)我們將服務(wù)端生成的隨機(jī)數(shù)放在 Cookie 中,前端需要從 Cookie 中自己提取出來(lái) _csrf 參數(shù),然后拼接成參數(shù)傳遞給后端,單純的將 Cookie 中的數(shù)據(jù)傳到服務(wù)端是沒(méi)用的。
理解透了上面兩點(diǎn),你就會(huì)發(fā)現(xiàn) _csrf 放在 Cookie 中是沒(méi)有問(wèn)題的,但是大家注意,配置的時(shí)候我們通過(guò) withHttpOnlyFalse 方法獲取了 CookieCsrfTokenRepository 的實(shí)例,該方法會(huì)設(shè)置 Cookie 中的 HttpOnly 屬性為 false,也就是允許前端通過(guò) js 操作 Cookie(否則你就沒(méi)有辦法獲取到 _csrf)。

配置完成后,重啟項(xiàng)目,此時(shí)我們就發(fā)現(xiàn)返回的 Cookie 中多了一項(xiàng):

接下來(lái),我們通過(guò)自定義登錄頁(yè)面,來(lái)看看前端要如何操作。

首先我們?cè)?resources/static 目錄下新建一個(gè) html 頁(yè)面叫做 login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.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>

這段 html 給大家解釋一下:
(1)首先引入 jquery 和 jquery.cookie ,方便我們一會(huì)操作 Cookie。
(2)定義三個(gè) input,前兩個(gè)是用戶(hù)名和密碼,第三個(gè)是登錄按鈕。
(3)點(diǎn)擊登錄按鈕之后,我們先從 Cookie 中提取出 XSRF-TOKEN,這也就是我們要上傳的 csrf 參數(shù)。
(4)通過(guò)一個(gè) POST 請(qǐng)求執(zhí)行登錄操作,注意攜帶上 _csrf 參數(shù)。

服務(wù)端我們也稍作修改,如下:

package com.mry.csrf.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/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());
    }
}

一方面這里給 js 文件放行。

另一方面配置一下登錄頁(yè)面,以及登錄成功的回調(diào),這里簡(jiǎn)單期間,登錄成功的回調(diào)我就給一個(gè)字符串就可以了。在登錄成功后回調(diào)的詳細(xì)解釋。

OK,所有事情做完之后,我們?cè)L問(wèn) login.html 頁(yè)面,輸入用戶(hù)名密碼進(jìn)行登錄,結(jié)果如下:

可以看到,我們的 _csrf 配置已經(jīng)生效了。

到此這篇關(guān)于SpringBoot 如何防御 CSRF 攻擊的文章就介紹到這了,更多相關(guān)SpringBoot 防御 CSRF 攻擊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 類(lèi)與對(duì)象詳細(xì)

    Java 類(lèi)與對(duì)象詳細(xì)

    這篇文章主要介紹了Java 類(lèi)與對(duì)象,在Java中,我們把對(duì)象的特征稱(chēng)為屬性,對(duì)象的用途稱(chēng)為方法,具有相同屬性和方法的對(duì)象,我們把他們歸為一類(lèi),簡(jiǎn)稱(chēng)類(lèi)。下面文章講詳細(xì)介紹什么是Java類(lèi)與對(duì)象,需要的朋友可以參考一下
    2021-10-10
  • Java布隆過(guò)濾器的原理和實(shí)現(xiàn)分析

    Java布隆過(guò)濾器的原理和實(shí)現(xiàn)分析

    數(shù)組、鏈表、樹(shù)等數(shù)據(jù)結(jié)構(gòu)會(huì)存儲(chǔ)元素的內(nèi)容,一旦數(shù)據(jù)量過(guò)大,消耗的內(nèi)存也會(huì)呈現(xiàn)線(xiàn)性增長(zhǎng)所以布隆過(guò)濾器是為了解決數(shù)據(jù)量大的一種數(shù)據(jù)結(jié)構(gòu)。本文就來(lái)和大家詳細(xì)說(shuō)說(shuō)布隆過(guò)濾器的原理和實(shí)現(xiàn),感興趣的可以了解一下
    2022-10-10
  • Java設(shè)計(jì)模式中的裝飾者模式

    Java設(shè)計(jì)模式中的裝飾者模式

    這篇文章主要介紹了Java設(shè)計(jì)模式中的裝飾者模式,裝飾者模式即Decorator?Pattern,裝飾模式是在不必改變?cè)?lèi)文件和使用繼承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能
    2022-07-07
  • JVM要雙親委派的原因及如何打破它

    JVM要雙親委派的原因及如何打破它

    平時(shí)做業(yè)務(wù)開(kāi)發(fā)比較少接觸類(lèi)加載器,但是如果想深入學(xué)習(xí),了解類(lèi)加載的原理是必不可少的.java的類(lèi)加載器有哪些?什么是雙親委派?為什么要雙親委派?如何打破它?接下來(lái)本文就帶大家詳細(xì)介紹這些知識(shí) ,需要的朋友可以參考下
    2021-06-06
  • 解決Java中socket使用getInputStream()阻塞問(wèn)題

    解決Java中socket使用getInputStream()阻塞問(wèn)題

    這篇文章主要介紹了解決Java中socket使用getInputStream()阻塞問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java遍歷讀取xml文件內(nèi)容

    java遍歷讀取xml文件內(nèi)容

    這篇文章主要為大家介紹了java遍歷讀取xml文件內(nèi)容,感興趣的小伙伴們可以參考一下
    2016-01-01
  • 淺談javap命令拆解字節(jié)碼文件

    淺談javap命令拆解字節(jié)碼文件

    這篇文章主要介紹了拆解字節(jié)碼文件javap命令,對(duì)反編譯感興趣的同學(xué)可以參考下
    2021-04-04
  • 深入了解Java接口回調(diào)機(jī)制

    深入了解Java接口回調(diào)機(jī)制

    這篇文章主要介紹了Java接口回調(diào)機(jī)制,下面我們來(lái)一起學(xué)習(xí)一下吧
    2019-05-05
  • Java實(shí)現(xiàn)最小高度樹(shù)

    Java實(shí)現(xiàn)最小高度樹(shù)

    本文主要介紹了Java實(shí)現(xiàn)最小高度樹(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • java仿微信搖一搖實(shí)現(xiàn)播放音樂(lè)

    java仿微信搖一搖實(shí)現(xiàn)播放音樂(lè)

    這篇文章主要為大家詳細(xì)介紹了java仿微信搖一搖實(shí)現(xiàn)播放音樂(lè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-06-06

最新評(píng)論