無狀態REST API中的CSRF攻擊

REST,Spring Boot
Remote
1
06:25 AM · Dec 01 ,2025

1. 概述

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

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

2. Does REST API Require CSRF Protection?

首先,我們可以找到一個 CSRF 攻擊的示例,在我們的專用指南中。

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

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

CSRF 漏洞取決於客户端存儲和發送這些憑據的方式。

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

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

2.1. Credentials Are Not Persisted

在我們從 REST API 檢索 token 之後,我們可以將 token 設置為 JavaScript 全局變量。這將保存 token 在瀏覽器內存中,並且它僅對當前頁面可用。

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

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

在移動瀏覽器上,即使瀏覽器進入後台,系統也會清除內存。

由於此選項對用户過於限制,因此此選項很少實施。

2.2. Credentials Stored in the Browser Storage

我們可以將 token 保存在瀏覽器存儲中——例如,會話存儲中。然後,我們的 JavaScript 客户端可以從其中讀取 token 並將 token 作為授權標頭髮送到所有 REST 請求。

這是一個常用的使用方式,例如使用 JWT:實施起來容易,並且可以防止攻擊者使用 CSRF 攻擊。 事實上,與 cookie 不同,瀏覽器存儲變量不會自動發送到服務器。

但是,此實現對 XSS 攻擊有漏洞:惡意 JavaScript 代碼可以訪問瀏覽器存儲並使用請求發送 token。在這種情況下,我們必須保護我們的應用程序。

2.3. Credentials Stored in Cookies

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

我們可以僅使用 cookie 存儲憑據,例如 JWT,但不用於身份驗證用户。

我們的 JavaScript 客户端必須讀取 token 並將其發送到 API 的授權標頭中。

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

但是,通過這樣做,我們的應用程序將對 XSS 攻擊有漏洞,就像在上一節中一樣。

另一種方法是使用會話 cookie 進行身份驗證,HTTP-only 標誌設置為 true。這通常由 Spring Security 提供,帶有 JSESSIONID cookie。當然,為了使 API 保持 stateless,我們永遠不應該在服務器端使用會話。

在這種情況下,我們的應用程序會像 stateful 應用程序一樣受到 CSRF 影響: 就像 cookie 會自動與任何 REST 請求一起發送一樣,點擊惡意鏈接可以執行身份驗證操作。

2.4. Other CSRF Vulnerable Configurations

某些配置不使用安全 token 作為憑據,但它們也可能受到 CSRF 攻擊。

這是 HTTP basic authenticationHTTP digest authenticationmTLS 的情況。

它們不太常見,但具有相同的缺點:瀏覽器會自動將憑據發送到任何 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 保護,我們可以通過使用 CookieCsrfTokenRepositorySecurityFilterChain bean 中發送 CSRF 令牌

必須將 HTTP-only 標誌設置為 false,以便從我們的 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" />

它將顯示:

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. 客户端配置

在客户端應用程序中,XSRF-TOKEN cookie 在第一次 API 訪問後設置。我們可以使用 JavaScript 正則表達式檢索它:

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

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

Spring 期望在 X-XSRF-TOKEN header 中接收它。 我們可以簡單地使用 JavaScript Fetch API 設置它:

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

現在,我們可以看到我們的請求正在工作,並且 “Invalid CSRF token” 錯誤已在 REST API 日誌中消失。

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

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

5. 結論

在本文中,我們回顧了 CSRF 攻擊針對 REST API 的各種可能和不可能的場景。

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

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

發佈 評論

Some HTML is okay.