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アプリケーションで認証するためにuserpasswordを提供する必要があります。

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プロジェクトにあります。