知識庫 / Spring / Spring Boot RSS 訂閱

無狀態 REST API 中的 CSRF 攻擊

REST,Spring Boot
HongKong
4
03:42 AM · Dec 06 ,2025

1. 概述

在上一篇文章中,我們已經解釋了 CSRF 攻擊如何影響 Spring MVC 應用程序。

本文將探討不同的情況,以確定 stateless REST API 是否可能受到 CSRF 攻擊,以及如果受到攻擊,如何保護它。

2. REST API 是否需要 CSRF 保護?

首先,我們可以參考我們的專門指南中一個 CSRF 攻擊的示例。

現在,閲讀完這份指南後,我們可能會認為 stateless REST API 不會受到這種攻擊的影響,因為服務器端沒有可竊取的 session。

讓我們以一個典型的例子為例:一個 Spring REST API 應用程序和一個 JavaScript 客户端。客户端使用安全令牌作為憑據(例如 JSESSIONID 或 JWT),該 REST API 在用户成功登錄後頒發。

CSRF 漏洞取決於客户端如何存儲和向 API 發送這些憑據。

讓我們回顧不同的選項以及它們將如何影響我們的應用程序的漏洞。

讓我們以一個典型的例子為例:一個 Spring REST API 應用程序和一個 JavaScript 客户端。客户端使用安全令牌作為憑據(例如 JSESSIONID 或 JWT),該 REST API 在用户成功登錄後頒發。

2.1. 憑據不持久化

一旦我們從 REST API 獲取了令牌,就可以將其設置為 JavaScript 全局變量。 這將在瀏覽器的內存中保存令牌,並且僅對當前頁面可用。

這是最安全的做法:CSRF 和 XSS 攻擊總是會導致客户端應用程序在新頁面上打開,而無法訪問用於簽入的初始頁面。

但是,我們的用户每次訪問或刷新頁面時都必須重新登錄。

在移動瀏覽器上,即使瀏覽器進入後台,系統也會清除內存,從而導致這種情況發生。

這種限制對於用户來説過於嚴格,因此此選項很少被實施。

2.2. 憑據存儲在瀏覽器存儲中

我們可以將我們的令牌存儲在瀏覽器存儲中——例如,會話存儲中。然後,我們的 JavaScript 客户端可以從中讀取令牌並將其包含在所有 REST 請求的授權標頭中。

這種方法是使用 JWT 的常見方式:它易於實現並且可以防止攻擊者利用 CSRF 攻擊。事實上,與 cookies 不同,瀏覽器存儲變量不會自動發送到服務器。

然而,這種實現方式容易受到 XSS 攻擊: 惡意 JavaScript 代碼可以訪問瀏覽器存儲並向請求中發送令牌。在這種情況下,我們必須保護我們的應用程序。

2.3. 憑據存儲在 Cookie 中

另一種選擇是使用 Cookie 永久存儲憑據。然後,我們的應用程序的漏洞取決於我們的應用程序如何使用 Cookie。

我們可以僅使用 Cookie 永久存儲憑據,例如 JWT,但不用於驗證用户。

我們的 JavaScript 客户端需要讀取令牌並將其發送到 API 的 Authorization 標頭中。

在這種情況下,我們的應用程序不受 CSRF 影響: 即使 Cookie 會自動通過惡意請求發送,我們的 REST API 仍會從 Authorization 標頭讀取憑據,而不是從 Cookie 中讀取。但是,必須將 HTTP-only 標誌設置為 false ,以便我們的客户端可以讀取 Cookie。

然而,通過這樣做,我們的應用程序將容易受到 XSS 攻擊,就像前一節所述

另一種方法是使用會話 Cookie 驗證請求,並且 HTTP-only 標誌設置為 true 。這通常由 Spring Security 提供,使用 JSESSIONID Cookie。當然,為了保持我們的 API 無狀態,我們永遠不應在服務器端使用會話。

在這種情況下,我們的應用程序容易受到 CSRF 影響,就像一個狀態完整的應用程序: 因為 Cookie 會自動與任何 REST 請求一起發送,所以惡意鏈接上的點擊可以執行已認證的操作。

2.4. 其他易受 CSRF 攻擊的配置

某些配置未使用安全令牌作為憑據,但仍可能受到 CSRF 攻擊的影響。

這包括 HTTP 基本身份驗證HTTP 摘要身份驗證mTLS

這些配置不常見,但卻存在相同的缺陷:瀏覽器會在任何 HTTP 請求中自動發送憑據。 在這些情況下,必須啓用 CSRF 保護。

3. 在 Spring Boot 中禁用 CSRF 保護

Spring Security 默認從版本 4 開始啓用 CSRF 保護。

如果我們的項目不需要它,可以在 SecurityFilterChain Bean 中禁用它:

@Configuration
public class SpringBootSecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();
        return http.build();
    }
}

4. 使用 REST API 啓用 CSRF 保護

4.1. Spring 配置

如果我們的項目需要 CSRF 保護,我們可以通過使用 <em><span>CookieCsrfTokenRepository</span></em><em><span>SecurityFilterChain</span></em> Bean 中發送 CSRF 令牌來實現。

我們必須將 <em>HTTP-only</em> 標誌設置為 <em>false</em>,以便從我們的 JavaScript 客户端檢索它。

@Configuration
public class SpringSecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        return http.build();
    }
}

重啓應用程序後,我們的請求接收到HTTP錯誤,這意味着CSRF保護已啓用。

通過將日誌級別調整為DEBUG,我們可以確認這些錯誤是由CsrfFilter類發出的。

<logger name="org.springframework.security.web.csrf" level="DEBUG" />
<div>
</div>
<div>
<p>It will display:</p>
Invalid CSRF token found for http://...

此外,我們還應該在瀏覽器中看到一個新的 XSRF-TOKEN cookie 存在。

讓我們在 REST 控制器中添加幾行代碼,以便將信息也寫入我們的 API 日誌中:

CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
LOGGER.info("{}={}", token.getHeaderName(), token.getToken());

4.2. 客户端配置

在客户端應用程序中,<em >XSRF-TOKEN</em> Cookie 在首次 API 訪問後設置。我們可以使用 JavaScript 正則表達式來檢索它:

const csrfToken = document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, '$1');

然後,我們必須將令牌發送到所有修改 API 狀態的 REST 請求:POST、PUT、DELETE 和 PATCH。

Spring 期望通過 X-XSRF-TOKEN 標頭接收該令牌。我們可以使用 JavaScript 的 Fetch API 簡單地設置它:

fetch(url, {
    method: 'POST',
    body: JSON.stringify({ /* data to send */ }),
    headers: { 'X-XSRF-TOKEN': csrfToken },
})

現在,我們可以看到我們的請求正在工作,並且在 REST API 日誌中不再出現 “無效 CSRF 令牌” 錯誤。

因此,攻擊者將無法執行 CSRF 攻擊。例如,嘗試從詐騙網站上執行相同請求的腳本將收到 “無效 CSRF 令牌” 錯誤。

確實,如果用户尚未訪問實際網站,則 Cookie 將不會設置,請求將失敗。

5. 結論

在本文中,我們回顧了 CSRF 攻擊針對 REST API 可能或不可能發生的各種情境。

然後,我們學習瞭如何使用 Spring Security 啓用或禁用 CSRF 保護。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.