Spring MVCとThymeleafによるCSRF保護
1. 前書き
Thymeleafは、HTML、XML、JavaScript、CSS、およびプレーンテキストを処理および作成するためのJavaテンプレートエンジンです。 ThymeleafとSpringの紹介については、this writeupをご覧ください。
この記事では、Thymeleafアプリケーションを使用してSpring MVCでprevent Cross-Site Request Forgery (CSRF) attacksを実行する方法について説明します。 具体的には、HTTP POSTメソッドのCSRF攻撃をテストします。
CSRFは、現在認証されているWebアプリケーションでエンドユーザーに不要なアクションを実行させる攻撃です。
2. Mavenの依存関係
最初に、ThymeleafとSpringを統合するために必要な構成を見てみましょう。 依存関係にはthymeleaf-springライブラリが必要です。
org.thymeleaf
thymeleaf
3.0.9.RELEASE
org.thymeleaf
thymeleaf-spring4
3.0.9.RELEASE
Spring 4プロジェクトの場合、thymeleaf-spring5の代わりにthymeleaf-spring4ライブラリを使用する必要があることに注意してください。 依存関係の最新バージョンはhereで見つかる可能性があります。
さらに、Spring Securityを使用するには、次の依存関係を追加する必要があります。
org.springframework.security
spring-security-web
5.0.6.RELEASE
org.springframework.security
spring-security-config
5.0.6.RELEASE
3. Java設定
hereをカバーする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 with Springシリーズを参照してください。
CSRF protection is enabled by default with Java configuration.この便利な機能を無効にするには、これをconfigure(…)メソッドに追加する必要があります。
.csrf().disable()
XML構成では、CSRF保護を手動で指定する必要があります。そうしないと、動作しません。
また、ログインフォームでログインページを使用している場合は、CSRFトークンを常に手動でコードの非表示パラメーターとしてログインフォームに含める必要があることに注意してください。
残りのフォームについては、CSRFトークンが非表示入力のフォームに自動的に追加されます。
4. ビューの構成
フォームアクションとテスト手順の作成を含むHTMLファイルの主要部分に進みましょう。 最初のビューでは、新しい学生をリストに追加しようとします:
Add Student
Add Student
このビューでは、id、name、gender、およびpercentageを提供することにより、リストに学生を追加しています(オプションで、フォームの検証に記載されています)。 このフォームを実行する前に、Webアプリケーションで認証するためにuserとpasswordを指定する必要があります。
4.1. ブラウザのCSRF攻撃テスト
次に、2番目のHTMLビューに進みます。 その目的は、CSRF攻撃を試みることです。
アクション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. 結論
この記事では、Spring SecurityとThymeleafフレームワークを使用してCSRF攻撃を防ぐ方法について説明しました。
このチュートリアルの完全な実装はthe GitHub projectにあります。これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。