最近中文字幕高清中文字幕无,亚洲欧美高清一区二区三区,一本色道无码道dvd在线观看 ,一个人看的www免费高清中文字幕

Spring Security 跨站請(qǐng)求偽造保護(hù)

1. 前言

很多小伙伴在開(kāi)發(fā) Spring Security 項(xiàng)目時(shí)候,本地測(cè)試都沒(méi)有問(wèn)題,一放到生產(chǎn)環(huán)境后,就會(huì)遇到「Invalid CSRF Token」問(wèn)題,這其實(shí)是 Spring Security 防止服務(wù)免受「跨站請(qǐng)求偽造」攻擊攻擊的防護(hù)行為。

圖片描述

跨站請(qǐng)求偽造(Cross Site Request Forgery),簡(jiǎn)寫(xiě)成「CSRF」或者「XSRF」,是一種挾持用戶(hù)所用瀏覽器,執(zhí)行非法操作的攻擊方法,也就是說(shuō),攻擊者利用「CSRF」漏洞偽造用戶(hù)操作,可實(shí)現(xiàn)例如購(gòu)物、注銷(xiāo)等效果,還可以利用該漏洞配合產(chǎn)生其他多種攻擊方式。

針對(duì)「CSRF」攻擊最經(jīng)濟(jì)的解決方式是增加「Referer」頭或者增加校驗(yàn)「Token」。

Spring Security 提供了針對(duì)「CSRF」的規(guī)范化防御手段,本節(jié)我們主要討論如何使用 Spring Security 的內(nèi)置功能防御「CSRF」攻擊。

2. CSRF 攻擊原理

我們用一個(gè)實(shí)例演示「CSRF」攻擊的過(guò)程。

圖片描述

假設(shè)我們登陸了一個(gè)銀行網(wǎng)站(bank.example.com),這個(gè)網(wǎng)站的作用是實(shí)現(xiàn)跨行轉(zhuǎn)賬的表單提交,通常情況下,我們會(huì)生成如下一個(gè) Form 表單。

<form method="post" action="/transfer">
  <!-- 匯款金額 -->
	<input type="text" name="amount"/>
  <!-- 匯款路由號(hào) -->
	<input type="text" name="routingNumber"/>
  <!-- 匯款賬戶(hù) -->
	<input type="text" name="account"/>

	<input type="submit" value="提交"/>
</form>

那我們發(fā)出的「post」請(qǐng)求格式可能如下:

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876

此時(shí),如果我們未登出,并且訪問(wèn)了其他惡意網(wǎng)站,并且其他惡意網(wǎng)站同樣包含了可提交的表單,表單形式如下:

<form method="post" action="https://bank.example.com/transfer">
  <!-- 隱藏項(xiàng)不可見(jiàn),轉(zhuǎn)賬金額,固定 100 元 -->
	<input type="hidden" name="amount" value="100.00"/>
  <!-- 隱藏項(xiàng)不可見(jiàn),轉(zhuǎn)賬路由碼 -->
	<input type="hidden" name="routingNumber" value="evilsRoutingNumber"/>
  <!-- 隱藏項(xiàng)不可見(jiàn),轉(zhuǎn)賬賬戶(hù) -->
	<input type="hidden" name="account" value="evilsAccountNumber"/>
  <!-- 可見(jiàn) -->
	<input type="submit" value="快來(lái)點(diǎn)我!"/>
</form>

當(dāng)我們很好奇,點(diǎn)擊了「快來(lái)點(diǎn)我」按鈕時(shí),我們會(huì)觸發(fā)轉(zhuǎn)賬請(qǐng)求,并將錢(qián)匯款到一個(gè)未知賬戶(hù)里。在這個(gè)過(guò)程中,雖然惡意網(wǎng)站并不知道我們的「Cookies」值,但是由于未登出,我們和銀行網(wǎng)站之間的 Cookies 還在,所以當(dāng)我們?cè)俅伟l(fā)起請(qǐng)求時(shí),該 Cookies 依然有效,這使得不知不覺(jué)被觸發(fā)的轉(zhuǎn)賬請(qǐng)求同樣有效。

除此之外,如果惡意網(wǎng)站使用 JS 腳本自動(dòng)提交表單的話(huà),用戶(hù)可能沒(méi)有任何被攻擊的感覺(jué)。

3. CSRF 攻擊的防御

我們雖然無(wú)法阻止惡意網(wǎng)站向目標(biāo)網(wǎng)站發(fā)送 HTTP 請(qǐng)求,但是我們可以確保惡意網(wǎng)站無(wú)法生成目標(biāo)網(wǎng)站所需的參數(shù),所以就出現(xiàn)了如下兩種常見(jiàn)的解決方案:

  • 使用同步「Token」模式
  • 在 Cookies 中指定網(wǎng)站同源的參數(shù)

這兩種方式在 Spring Security 中都已支持。

3.1 CSRF 保護(hù)的前提

要實(shí)現(xiàn) CSRF 保護(hù),首先我們要確保安全方法是冪等的。安全方法包括「GET」,「HEAD」,「OPTIONS」,「TRACE」,冪等是指這些方法在反復(fù)發(fā)送后服務(wù)器狀態(tài)不會(huì)改變。

3.2 同步「Token」模式

這種方法時(shí)防御「CSRF」攻擊的最常用手段,它的原理是確保項(xiàng)目表網(wǎng)站發(fā)送的請(qǐng)求中,每次都包含一個(gè)被稱(chēng)為「CSRF Token」的隨機(jī)參數(shù)。

每當(dāng)請(qǐng)求提交到服務(wù)端后,服務(wù)端會(huì)對(duì)比請(qǐng)求中包含的「CSRF Token」和期望的是否匹配,如果不匹配,此請(qǐng)求作廢。這里的關(guān)鍵是,「CSRF Token」不能由瀏覽器生成。

來(lái)看一下代碼:

<form method="post" action="/transfer">
  <!-- csrf 頭 -->
	<input type="hidden" name="_csrf" value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
  <!-- 匯款金額 -->
	<input type="text" name="amount"/>
  <!-- 匯款路由號(hào) -->
	<input type="text" name="routingNumber"/>
  <!-- 匯款賬戶(hù) -->
	<input type="text" name="account"/>
	<input type="submit" value="提交"/>
</form>

上述代碼中增加了 _csrf 參數(shù),且該值由服務(wù)器生成并埋在頁(yè)面表單中,此時(shí) HTTP 請(qǐng)求的內(nèi)容如下:

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

由于瀏覽器同源策略,我們不用擔(dān)心惡意網(wǎng)站可以得到 _csrf 的值,而且惡意網(wǎng)站無(wú)法偽造出于服務(wù)器想匹配的「CSRF Token」,這樣就保護(hù)了用戶(hù)信息的安全。

3.3 SameSite 參數(shù)

另一種避免「CSRF」攻擊的方法是使用 SameSite 參數(shù)。這是一種新的方式,有賴(lài)于瀏覽器的支持。這種情況下服務(wù)端會(huì)指定一個(gè) SameSite 參數(shù)來(lái)保障請(qǐng)求不會(huì)來(lái)自于非可信網(wǎng)站。

例如,包含 SameSite 參數(shù)的響應(yīng)消息如下:

Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax

其中 SameSite 屬性支持的值包括:

  • 嚴(yán)格模式(Strict)。僅 URL 相匹配的網(wǎng)站才支持發(fā)送 Cookie,任何其他網(wǎng)站都不會(huì)發(fā)生 Cookie。
  • 寬松模式(Lax)。相比嚴(yán)格模式,寬松模式下支持部分非相同網(wǎng)站的請(qǐng)求中攜帶 Cookie,比如超鏈接方式跳轉(zhuǎn)、預(yù)加載、GET請(qǐng)求。

采用這種方式后,來(lái)自惡意網(wǎng)站的請(qǐng)求無(wú)法加上 JSESSIONID 參數(shù),由此避免了「CSRF」攻擊。

4. Spring Security 配置方法

默認(rèn)情況下,Spring Security 已開(kāi)啟「CSRF」保護(hù),這里我們羅列一下其它常用配置。

4.1 自定義 Token 倉(cāng)庫(kù)

默認(rèn)情況下,CSRF Token 存儲(chǔ)在 HttpSession 中,使用 HttpSessionCsrfTokenRepository 對(duì)象維護(hù)。如果需要進(jìn)行擴(kuò)展,比如不僅要在 HTTP 請(qǐng)求中攜帶 Token,也需要在 JS 應(yīng)用中應(yīng)用 Token,那需要通過(guò)如下方式:

在配置類(lèi)中構(gòu)造并注入 CookieCsrfTokenRepository 對(duì)象。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) {
        http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()));
    }
}

4.2 禁用 CSRF 保護(hù)

默認(rèn)情況下,CSRF 保護(hù)功能已被開(kāi)啟,如果需要關(guān)閉,可通過(guò)如下方式配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) {
        http.csrf(csrf -> csrf.disable());
    }
}

4.3 攜帶 CSRF Token

為了在 HTTP 請(qǐng)求中攜帶 CSRF Token,我們必須要對(duì) HTTP Request 做一些配置,因?yàn)樗J(rèn)是不會(huì)攜帶 CSRF 相關(guān)參數(shù)的。默認(rèn)情況下,Spring Security 中有 CsrfFilter 判斷請(qǐng)求中是否有 _csrf 參數(shù),通常請(qǐng)求來(lái)自于兩種情況,F(xiàn)orm 表單提交或者 Ajax。

4.3.1 Form 表單提交

使用 Form 表單提交代碼時(shí),我們需要在 Form 參數(shù)中增加一個(gè)隱藏項(xiàng):_csrf,例如:

<input type="hidden"
    name="_csrf"
    value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>

這里的 _csrf 有幾種配置方式:

  • 自動(dòng)注入

    Spring Security 通過(guò)擴(kuò)展 Spring 的 RequestDataValueProcessor 類(lèi),實(shí)現(xiàn)了 RequestDataValueProcessor 類(lèi),這意味著如果我們使用 Spring 標(biāo)簽庫(kù)、Thymeleaf 模板插件、或者其它集成了 RequestDataValueProcessor 對(duì)象的視圖組件是,表單的非冪等請(qǐng)求(例如:POST)都會(huì)自動(dòng)攜帶 CSRF Token。

  • JSP 標(biāo)簽

    針對(duì) JSP 作為頁(yè)面開(kāi)發(fā)基礎(chǔ),我們可以直接使用 Spring 的表單標(biāo)簽庫(kù)或者 CsrfInput 標(biāo)簽。也可以通過(guò)更加直接的方式,在使用 HttpServletRequest 屬性 _csrf,代碼如下:

    <c:url var="logoutUrl" value="/logout"/>
    <form action="${logoutUrl}" method="post">
    <input type="submit" value="登出" />
    <input type="hidden"
        name="${_csrf.parameterName}"
        value="${_csrf.token}"/>
    </form>
    

4.3.2 Ajax 和 JSON 請(qǐng)求

如果使用 Javascript 做為請(qǐng)求提交方式,我們沒(méi)法直接使用 Http CSRF 參數(shù),取而代之的是使用 Http 頭的方式。這同樣也有幾種方法:

  • 自動(dòng)注入

    Spring Security 可以自動(dòng)將 CSRF Token 保存到 Cookie 中,一些客戶(hù)端框架如 AngularJS 會(huì)自動(dòng)從中得到 CSRF Token 并放置到請(qǐng)求頭中。

  • Meta 標(biāo)簽

    另一種方式是從 Cookie 中解壓 Token 并使用 Meta 標(biāo)簽,如下:

    <html>
    <head>
        <meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
        <meta name="_csrf_header" content="X-CSRF-TOKEN"/>
        <!-- ... -->
    </head>
    

    當(dāng) Meta 標(biāo)簽中有 Token 信息時(shí),我們就可以將 Meta 中的 CSRF Token 值用作請(qǐng)求參數(shù)了。以 JQuery 為例:

    $(function () {
        var token = $("meta[name='_csrf']").attr("content");
        var header = $("meta[name='_csrf_header']").attr("content");
        $(document).ajaxSend(function(e, xhr, options) {
            xhr.setRequestHeader(header, token);
        });
    });
    

5. 小結(jié)

本節(jié)我們討論了 CSRF 的含義及在 Spring Security 中配置方法:

  • CSRF 是一種常見(jiàn)的 B/S 攻擊形式;
  • CSRF 可以在瀏覽器上偽造用戶(hù)的請(qǐng)求;
  • CSRF 的防御思路是確保發(fā)送請(qǐng)求的來(lái)源是可信的;
  • CSRF 的防御方法包含「同步 Token」和「設(shè)置 SameSite 參數(shù)」兩種,其中「SameSite」參數(shù)方式需要瀏覽器的支持;
  • Spring Security 默認(rèn)已開(kāi)啟 CSRF 保護(hù)。

下節(jié)我們討論 Spring Security 對(duì) HTTP 請(qǐng)求常用的和安全相關(guān)的頭部信息。