Spring SecurityによるCSRFおよびCORSの設定

前回に引き続き、Spring BootとAzure Cosmos DBを使って蔵書管理アプリケーションを作成にチャレンジしていく。今回作ろうとしている蔵書管理Webアプリは下図のような構成になる。

  • 蔵書管理アプリケーションの概要

    蔵書管理アプリケーションの概要

フロントエンドとバックエンドはAzure内の別のサービスを使ってデプロイし、その間はWeb APIによってコミュニケーションする構成である。この時、フロントエンドのJavaScriptはバックエンドのSpring Bootアプリに対してXMLHttpRequestを利用してリクエストを行いたい。しかし、セキュリティ上の理由から、異なるオリジン(スキーム、ホスト、ポート番号などの組み合わせ)間のスクリプトによる通信には制限が設けられている。

今回の構成はこの制限に該当するため、サーバ側で異なるオリジンからのリクエストを受け付けるように設定を加える必要がある。このように、異なるオリジンからのリソースアクセスを許可する手法をCORS(Cross-Origin Resource Sharing)と呼ぶ。JavaによるWebアプリケーションでは、Spring Securityを使うことで比較的簡単にその設定を行える。

本稿の例では、Spring Initializrで雛形を作成した際にDependenciesにSpring Securityを追加しておいたが、追加してしない場合はpom.xmlに下記の依存関係の設定を加えておこう。

pom.xmlのSpring Securityの設定

    <dependency> 
      <groupId>org.springframework.security</groupId>  
      <artifactId>spring-security-test</artifactId>  
    </dependency> 

Spring Securityには大きく分けてXMLで設定を追加する方法と、Javaプログラムで設定を実装する方法があるが、今回は後者を使用する。Webアプリに対するセキュリティ設定を追加するには、次のようにWebSecurityConfigurerAdapterをextendsしてconfigure()メソッドをオーバーライドすればよい。

BooklistSecurityConfig.java

package jp.mynavi.azurejava.booklist.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@EnableWebSecurity
public class BooklistSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/**").permitAll() // (1) /api/以下は全てのユーザからのアクセスを許可
                .anyRequest().authenticated()       //     それ以外は認証されたユーザのみアクセスを許可
                .and()
                .csrf().ignoringAntMatchers("/api/**")  // (2) /api/以下はCSRFチェックを除外
                .and()
                .cors()                         // (3) CORSの設定を有効にする
                .configurationSource(this.corsConfigurationSource());   // 
    }

    private CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedMethods(Arrays.asList("POST","GET"));   // (4) 許可するHTTPメソッド
        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);          // (5) すべてのHTTPヘッダを許可する
        corsConfiguration.addAllowedOrigin("http://localhost:8081");            // (6)許可するオリジン(ローカルテスト用)
        corsConfiguration.addAllowedOrigin("https://XXXXXXXX.web.core.windows.net");    // (7) 許可するオリジン(Azureストレージ用)

        UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
        corsSource.registerCorsConfiguration("/api/**", corsConfiguration);     // (8) このフィルタの対象URL

        return corsSource;
    }
}

各セキュリティ設定は、http.authorizeRequests()メソッドの戻り値に対してメソッドをつなげていくことで行える。(1)の部分では、指定されたURLに対するアクセスに対して認証が必要か否かを設定している。今回のアプリではエンドポイントはすべて/api/以下に設定されているので、/api/以下のみ認証なしでアクセスできるようにしてある。

Spring SecurityではデフォルトでCSRF(Cross-Site Request Forgery)への対策が有効になっている。CSRFとは、第三者のサイトから、ユーザが意図しない形で攻撃対象のWebサービスに対してリクエストを送信させる攻撃手法である。今回は第三者サイトからのリクエストを受け付けるために(2)で/api/以下へのアクセスのみCRSFのチェックを除外している。

(3)の2行では、CORSの設定を有効にしている。実際の設定内容はcorsConfigurationSource()メソッドで実装されており、その戻り値としてCorsConfigurationSourceオブジェクトを受け取ってセットする形になっている。

CORSに関する設定はCorsConfigurationクラスを用いて行う。まず、(4)ではCORSを許可するHTTPメソッドの種類を指定している。続く(5)では、CORSの際にすべてのHTTPヘッダを使用できるように許可している。デフォルトでは、CORSの際には特定のHTTPヘッダした使用することができない。

(6)と(7)は、CORSを許可する対象のオリジンの設定である。http://localhost:8081/ はテスト用にローカルで実行した時のために追加している。(7)のところには、蔵書管理アプリのフロントエンドの公開用URLを指定してCORSを許可する。この部分のURLは、フロントエンドの静的サイトをデプロイした際に、各自のデプロイ先のURLに合わせて変更してもらいたい。

一通りの設定を反映させたCorsConfigurationオブジェクトは、最終的にCorsConfigurationSourceオブジェクトにセットして呼び出し元に返す。今回はURLベースで対象を指定することができるUrlBasedCorsConfigurationSourceクラスを使用して、(8)で対象URLを指定しながらセットしている。この場合、/api/以下へのアクセスだけが今回のCORS許可の設定が有効な対象となる。

Webサイト公開用のAzureストレージの準備

蔵書管理アプリのフロントエンドは、HTMLとJavaScriptで作成して静的なWebサイトとして公開する。Azureでは、ストレージサービスのAzureストレージを静的なWebサイトのホスト先として利用することができる。

Azureストレージを使用するには、まずAzureポータルから[ストレージ アカウント]のページを開いて、[追加]ボタンで新規のストレージアカウントを作成する。下図はストレージアカウントの設定例である。リソースグループは、App Service用と同じものを選ぶ。アカウント名は任意の名前を設定してほしい。アカウントの種類は「StorageV2」でよい。StorageV1やBlobStorageは、従来のアカウント種類との互換性のために残されているもので、現在は可能な限りStorageV2を使用することが推奨されている。

ネットワークの設定では、次のように[パブリック エンドポイント (すべてのネットワーク)]にチェックが入っていることを確認する。

詳細やタグの設定は、特に理由がなければデフォルトのままでよい。準備ができたら、[確認および作成]ボタンをクリックして、設定内容を確認した上でアカウントの作成を実行しよう。

アカウントの作成が完了すると、次のように「デプロイが完了しました」と表示される。

[リソースに移動]をクリックすれば、作成したストレージアカウントの詳細設定ページに移動できる。このページからデータ格納用のコンテナの作成などができるが、今回のように静的Webサイトをホストしたい場合には、このページではなく左のメニューから「静的なWebサイト」を選択する。

下図のようなページが表示されるので、ここでデフォルトでは[無効]になっている「静的なWebサイト」の設定を[有効]に変更する。

静的なWebサイトの設定が有効になると、「$web」という名前のコンテナが自動で作成され、次のようにWebサイトにアクセスするためのエンドポイントのURLが設定される。

今回は、このうちの「プライマリエンドポイント」のURLを、さきほどのSpring SecurityのCORSを許可するオリジンとして設定しよう。それによって、このAzureストレージの$webコンテナにホストされたWebサイトから、蔵書管理アプリのWeb APIへのリクエストが可能になる。

さて、次回は蔵書管理アプリのフロントエンド部分を作成して、いま用意したAzureストレージの$webコンテナにデプロイしてみる。