Spring MVC 和 Thymeleaf 中 CSRF 保護
Spring MVC,Spring Security
1. 簡介
Thymeleaf 是一種 Java 模板引擎,用於處理和創建 HTML、XML、JavaScript、CSS 和純文本。有關 Thymeleaf 和 Spring 的介紹,請參閲此文章。
在本文中,我們將討論如何防止跨站請求偽造 (CSRF) 攻擊在 Spring MVC 中使用 Thymeleaf 應用程序。具體來説,我們將測試 HTTP POST 方法的 CSRF 攻擊。
CSRF 是一種攻擊,強制用户在當前已認證的 Web 應用程序中執行未經授權的操作。
2. Maven 依賴
首先,讓我們看看將 Thymeleaf 與 Spring 集成所需的配置。<em>thymeleaf-spring</em> 庫需要在我們的依賴中:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
請注意,對於 Spring 4 項目,必須使用 thymeleaf-spring4 庫,而不是 thymeleaf-spring5 庫。 依賴項的最新版本可以在 這裏找到。
此外,為了使用 Spring Security,我們需要添加以下依賴項:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.7.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.7.3</version>
</dependency>
最新版本的兩個與 Spring Security 相關的庫已發佈,可從 這裏 和 這裏 下載。
3. Java 配置
除了此處涵蓋的 Thymeleaf 配置外,還需要添加 Spring Security 的配置。為此,我們需要創建一個類:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebMVCSecurity {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withUsername("user1")
.password("{noop}user1Pass")
.authorities("ROLE_USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
.antMatchers("/resources/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
return http.build();
}
}
對於安全配置的更多詳細信息和描述,請參考“Spring 安全”系列文檔。
默認情況下,通過 Java 配置啓用了 CSRF 保護。 要禁用此有用的功能,需要在 configure(…)方法中添加以下內容:
.csrf().disable()
在XML配置中,我們需要手動指定CSRF保護;否則將無法正常工作:
<security:http
auto-config="true"
disable-url-rewriting="true"
use-expressions="true">
<security:csrf />
<!-- Remaining configuration ... -->
</security:http>
請注意,如果我們在使用帶有包含 PATCH、POST、PUT 或 DELETE 等 HTTP 謂詞的登錄或註銷頁面,並且該頁面包含一個表單,則必須始終在代碼中手動作為隱藏參數包含 CSRF 令牌。
登錄表單中使用 CSRF 令牌的示例:
<form name="login" th:action="@{/login}" method="post">
...
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
</form>
對於剩餘的表單,如果使用 <em th:action>,則表單中將自動添加 CSRF 令牌。
<input
type="hidden"
name="_csrf"
value="32e9ae18-76b9-4330-a8b6-08721283d048" />
<!-- Example token -->
4. 視圖配置
讓我們繼續到 HTML 文件中關於表單操作和測試流程創建的主要部分。 在第一個視圖中,我們嘗試將新學生添加到列表中:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
<h1>Add Student</h1>
<form action="#" th:action="@{/saveStudent}" th:object="${student}"
method="post">
<ul>
<li th:errors="*{id}" />
<li th:errors="*{name}" />
<li th:errors="*{gender}" />
<li th:errors="*{percentage}" />
</ul>
<!-- Remaining part of HTML -->
</form>
</body>
</html>
從這個角度來看,我們通過提供 id、name、gender 和 percentage(如表單驗證中所述,可選)將學生添加到列表中。在執行此表單之前,我們需要提供 user 和 password 以在 Web 應用程序中進行身份驗證。
4.1. 瀏覽器 CSRF 攻擊測試
現在我們將轉向第二個 HTML 視圖。其目的是嘗試進行 CSRF 攻擊:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<form action="http://localhost:8080/spring-thymeleaf/saveStudent" method="post">
<input type="hidden" name="payload" value="CSRF attack!"/>
<input type="submit" />
</form>
</body>
</html>
我們知道操作 URL 是 http://localhost:8080/spring-thymeleaf/saveStudent。黑客試圖訪問該頁面以執行攻擊。
為了測試,在另一個瀏覽器中打開 HTML 文件,而不登錄應用程序。當您嘗試提交表單時,我們會收到以下頁面:
由於我們發送了缺少 CSRF 令牌的請求,因此我們的請求被拒絕。
請注意,HTTP 會話用於存儲 CSRF 令牌。當請求發送時,Spring 會將生成的令牌與會話中存儲的令牌進行比較,以確認用户未被入侵。
4.2. JUnit CSRF 攻擊測試
如果您不想使用瀏覽器測試 CSRF 攻擊,也可以通過快速集成測試進行,我們先從 Spring 配置開始:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {
WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class })
public class CsrfEnabledIntegrationTest {
// configuration
}
然後開始進行實際的測試:
@Test
public void addStudentWithoutCSRF() throws Exception {
mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON)
.param("id", "1234567").param("name", "Joe").param("gender", "M")
.with(testUser())).andExpect(status().isForbidden());
}
@Test
public void addStudentWithCSRF() throws Exception {
mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON)
.param("id", "1234567").param("name", "Joe").param("gender", "M")
.with(testUser()).with(csrf())).andExpect(status().isOk());
}
第一個測試將導致由於缺少 CSRF 令牌而返回禁止狀態,而第二個則會正常執行。
5. 結論
在本文中,我們探討了如何使用 Spring Security 和 Thymeleaf 框架來防止 CSRF 攻擊。
0 位用戶收藏了這個故事!