SpringMVCとThymeleafによるCSRF保護
1. 序章
Thymeleaf は、HTML、XML、JavaScript、CSS、およびプレーンテキストを処理および作成するためのJavaテンプレートエンジンです。 ThymeleafとSpringの紹介については、この記事をご覧ください。
この記事では、Thymeleafアプリケーションを使用したSpring MVCでクロスサイトリクエストフォージェリ(CSRF)攻撃を防止する方法について説明します。 具体的には、HTTPPOST方式のCSRF攻撃をテストします。
CSRFは、現在認証されているWebアプリケーションでエンドユーザーに不要なアクションを実行させる攻撃です。
2. Mavenの依存関係
まず、ThymeleafをSpringと統合するために必要な構成を見てみましょう。 thymeleaf-spring ライブラリは、依存関係に必要です。
<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-spring5の代わりにthymeleaf-spring4ライブラリを使用する必要があることに注意してください。 依存関係の最新バージョンはここにあります。
さらに、Spring Securityを使用するには、次の依存関係を追加する必要があります。
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.6.0</version>
</dependency>
2つのSpringSecurity関連ライブラリの最新バージョンはこことここで入手できます。
3. Java構成
ここでカバーされているThymeleaf構成に加えて、SpringSecurityの構成を追加する必要があります。 そのためには、クラスを作成する必要があります。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebMVCSecurity extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("{noop}user1Pass")
.authorities("ROLE_USER");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
セキュリティ構成の詳細と説明については、 Security withSpringシリーズを参照してください。
CSRF保護は、Java構成でデフォルトで有効になっています。この便利な機能を無効にするには、 configure(…)メソッドにこれを追加する必要があります。
.csrf().disable()
XML構成では、CSRF保護を手動で指定する必要があります。そうしないと、機能しません。
<security:http
auto-config="true"
disable-url-rewriting="true"
use-expressions="true">
<security:csrf />
<!-- Remaining configuration ... -->
</security:http>
また、ログインフォームでログインページを使用している場合は、コードに手動で非表示のパラメーターとしてCSRFトークンをログインフォームに含める必要があることにも注意してください。
<input
type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
残りのフォームについては、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 、利用者、パーセンテージ(オプションで、フォームの検証に記載されています)。 このフォームを実行する前に、Webアプリケーションで認証するためにuserとpasswordを提供する必要があります。
4.1. ブラウザのCSRF攻撃テスト
次に、2番目の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トークンなしでリクエストを送信したため、リクエストは拒否されました。
CSRFトークンを保存するためにHTTPセッションが使用されることに注意してください。 リクエストが送信されると、Springは、ユーザーがハッキングされていないことを確認するために、生成されたトークンをセッションに保存されているトークンと比較します。
4.2. JUnitCSRF攻撃テスト
ブラウザを使用して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トークンがないために禁止ステータスになりますが、2番目のテストは適切に実行されます。
5. 結論
この記事では、SpringSecurityとThymeleafフレームワークを使用してCSRF攻撃を防ぐ方法について説明しました。
このチュートリアルの完全な実装は、GitHubプロジェクトにあります。