第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Spring Security 跨站請求偽造保護

1. 前言

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

圖片描述

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

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

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

2. CSRF 攻擊原理

我們用一個實例演示「CSRF」攻擊的過程。

圖片描述

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

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

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

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

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

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

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

當我們很好奇,點擊了「快來點我」按鈕時,我們會觸發(fā)轉(zhuǎn)賬請求,并將錢匯款到一個未知賬戶里。在這個過程中,雖然惡意網(wǎng)站并不知道我們的「Cookies」值,但是由于未登出,我們和銀行網(wǎng)站之間的 Cookies 還在,所以當我們再次發(fā)起請求時,該 Cookies 依然有效,這使得不知不覺被觸發(fā)的轉(zhuǎn)賬請求同樣有效。

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

3. CSRF 攻擊的防御

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

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

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

3.1 CSRF 保護的前提

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

3.2 同步「Token」模式

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

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

來看一下代碼:

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

上述代碼中增加了 _csrf 參數(shù),且該值由服務(wù)器生成并埋在頁面表單中,此時 HTTP 請求的內(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

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

3.3 SameSite 參數(shù)

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

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

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

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

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

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

4. Spring Security 配置方法

默認情況下,Spring Security 已開啟「CSRF」保護,這里我們羅列一下其它常用配置。

4.1 自定義 Token 倉庫

默認情況下,CSRF Token 存儲在 HttpSession 中,使用 HttpSessionCsrfTokenRepository 對象維護。如果需要進行擴展,比如不僅要在 HTTP 請求中攜帶 Token,也需要在 JS 應(yīng)用中應(yīng)用 Token,那需要通過如下方式:

在配置類中構(gòu)造并注入 CookieCsrfTokenRepository 對象。

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

4.2 禁用 CSRF 保護

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

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

4.3 攜帶 CSRF Token

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

4.3.1 Form 表單提交

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

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

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

  • 自動注入

    Spring Security 通過擴展 Spring 的 RequestDataValueProcessor 類,實現(xiàn)了 RequestDataValueProcessor 類,這意味著如果我們使用 Spring 標簽庫、Thymeleaf 模板插件、或者其它集成了 RequestDataValueProcessor 對象的視圖組件是,表單的非冪等請求(例如:POST)都會自動攜帶 CSRF Token。

  • JSP 標簽

    針對 JSP 作為頁面開發(fā)基礎(chǔ),我們可以直接使用 Spring 的表單標簽庫或者 CsrfInput 標簽。也可以通過更加直接的方式,在使用 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 請求

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

  • 自動注入

    Spring Security 可以自動將 CSRF Token 保存到 Cookie 中,一些客戶端框架如 AngularJS 會自動從中得到 CSRF Token 并放置到請求頭中。

  • Meta 標簽

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

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

    當 Meta 標簽中有 Token 信息時,我們就可以將 Meta 中的 CSRF Token 值用作請求參數(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 是一種常見的 B/S 攻擊形式;
  • CSRF 可以在瀏覽器上偽造用戶的請求;
  • CSRF 的防御思路是確保發(fā)送請求的來源是可信的;
  • CSRF 的防御方法包含「同步 Token」和「設(shè)置 SameSite 參數(shù)」兩種,其中「SameSite」參數(shù)方式需要瀏覽器的支持;
  • Spring Security 默認已開啟 CSRF 保護。

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