コンテンツにスキップ

CORS 環境の構築⚓︎

CORS (オリジン間リソース共有)とは⚓︎

CORS とは、いくつかの HTTP ヘッダーを使用することで、同一オリジンポリシーの制限を回避する仕組みです。

オリジンとは

オリジンとは、 URL のスキーム(プロトコル)、ホスト(ドメイン)、ポート番号の部分を指します( Origin (オリジン) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN )。

  • http://localhosthttps://localhost はスキームが異なるので異なるオリジン
  • https://www.example.comhttps://www2.example.com はホスト部分が異なるので異なるオリジン
  • https://localhost:4431https://localhost:4432 はポート番号が異なるので異なるオリジン
同一オリジンポリシーとは

ブラウザーは原則として「同一オリジンポリシー」で動作します( 同一オリジンポリシー - ウェブセキュリティ | MDN )。

同一オリジンポリシーは重要なセキュリティの仕組みであり、あるオリジンによって読み込まれた文書やスクリプトが、他のオリジンにあるリソースにアクセスできる方法を制限するものです。

つまり、 https://aaa.example.com から取得したリソース( HTML 文書や JavaScript )から、 https://bbb.example.net のリソース( Web API や HTML 文書)には原則としてアクセスできません。

本章で解説する CORS 環境とは、 CSR アプリケーションにおいて、フロントエンドアプリケーションとバックエンドアプリケーションの配置されるサーバーのオリジンが異なる環境を意味します。 CORS 環境で CSR アプリケーションを構築する場合、いくつかの考慮や追加の実装が必要です。

CORS の仕組みの詳細は「 オリジン間リソース共有 (CORS) - HTTP | MDN 」を参照してください。

バックエンドアプリケーション( Spring Boot )⚓︎

Spring Boot アプリケーションでは、 SecurityFilterChain CORS に関するポリシーを設定します。 AlesInfiny Maia OSS Edition (以降『 AlesInfiny Maia 』)では、許可するオリジンの一覧をアプリケーション設定ファイル application-<環境名>.properties から取得します。

許可するオリジンの追加⚓︎

本番環境で許可するオリジンの一覧を Web 層のアプリケーション設定ファイル application-prd.properties に記述します。 許可するオリジンが複数ある場合には、 , で区切ります。

application-prd.properties
1
cors.allowed.origins=https://frontend.example.com,https://sub.frontend.example.com

なお、開発時にのみ使用する設定は、 application-dev.properties に記述します。

application-dev.properties
1
cors.allowed.origins=https://dev.frontend.example.com

許可するオリジンの読み込み⚓︎

まず、アプリケーション設定ファイルに記述した許可対象オリジンを読み込む設定を実施します。 具体的には、@ConfigurationProperties を利用し、 application-<環境名>.propertiescors.allowed.origins に設定した値を CorsAllowedOriginsProperties クラスに対応付けています。

許可するオリジンを読み込む CorsAllowedOriginsProperties.java の実装例

CorsAllowedOriginsProperties.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@ConfigurationProperties(prefix = "cors.allowed")
public class CorsAllowedOriginsProperties {

  private List<String> origins = new ArrayList<>();

  public List<String> getOrigins() {
    return List.copyOf(origins);
  }

  public void setOrigins(List<String> origins) {
    this.origins = origins == null ? new ArrayList<>() : new ArrayList<>(origins);
  }
}

CORS ポリシーの設定⚓︎

Spring Boot では、 CORS に関する設定を SecurityFilterChain を利用して実装します。

以下は、サンプルアプリケーションにおける CORS の設定を実現する WebSecurityConfig.java の実装例です。

WebSecurityConfig.javaCORS 設定例
WebSecurityConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.dressca.web.consumer.security;

import com.dressca.web.security.CorsAllowedOriginsProperties;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import lombok.RequiredArgsConstructor;

/**
 * セキュリティ関連の実行クラスです。
 */
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {

  private final CorsAllowedOriginsProperties corsAllowedOriginsProperties;

  /**
   * CORS 有効化、認可機能を設定します。
   *
   * @param http http リクエスト。
   * @return フィルターチェーン。
   * @throws Exception 例外。
   */
  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.deny())
        .contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'none';")))
        .securityMatcher("/api/**")
        // CSRF トークンを利用したリクエストの検証を無効化( OAuth2.0 による認証認可を利用する前提のため)
        // OAuth2.0 によるリクエストの検証を利用しない場合は、有効化して CSRF 対策を施す
        .csrf(csrf -> csrf.ignoringRequestMatchers("/api/**")).cors(Customizer.withDefaults());

    return http.build();
  }

  /**
   * CORS 設定を Spring Security へ提供します。
   *
   * @return CORS 設定ソース。
   */
  @Bean
  public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowCredentials(true);
    configuration.setAllowedOrigins(corsAllowedOriginsProperties.getOrigins());
    configuration.setAllowedMethods(List.of("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"));
    configuration.setAllowedHeaders(List.of("*"));
    // 注文情報の確定にLocationを利用するため公開ヘッダーとして設定
    configuration.addExposedHeader("Location");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", configuration);
    return source;
  }
}

まず、@EnableWebSecurity を付与することで、このクラスが Spring Security の設定クラスであることを示します。 また、 application-prd.properties から読み込んだ許可対象オリジンの一覧を CORS のポリシー設定に渡すため、 CorsAllowedOriginsProperties を定義します。

さらに、上記の実装は securityFilterChain メソッドと corsConfigurationSource メソッドがそれぞれ別の役割を持っています。

  • securityFilterChain メソッド

    Spring Security のフィルターチェーンを構成するメソッドです。 .cors(Customizer.withDefaults()) を指定することで、 Spring Security に対して CORS の処理を有効化し、 CorsConfigurationSource Bean から設定を取得するようにしています。

  • corsConfigurationSource メソッド

    CORS で許可するオリジン、 HTTP メソッド、 HTTP ヘッダーなどの具体的なポリシーを定義するメソッドです。 securityFilterChain メソッドで有効化された CORS 処理は、このメソッドが返す CorsConfigurationSource の内容に従って動作します。

CORS のポリシー設定についての詳細⚓︎

上のコード例「 WebSecurityConfig.java 」における CORS の設定に関するメソッドについて説明します。

  • setAllowCredentials メソッド

    許可したオリジンのクライアントに Cookie 等の認証情報を送信することを許可します。 アプリケーションで Cookie や認証を使用する場合、このメソッドの呼び出しが必要です。

  • setAllowedOrigins メソッド

    CORS でリソースへのアクセスを許可するオリジンを設定します。 AlesInfiny Maia では CorsAllowedOriginsPropertiesapplication-<環境名>.properties から読み込んだ値を引数に渡します。

  • setAllowedMethods メソッド

    許可したオリジンのクライアントが使用可能な HTTP メソッドを設定します。アプリケーションで許可する HTTP メソッド名を指定してください。なお、 CORS 環境の場合プリフライトリクエストが使用する OPTIONS は必須です。詳細は Preflight request (プリフライトリクエスト) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN を参照してください。

  • setAllowedHeaders メソッド

    許可したオリジンのクライアントに許可する HTTP リクエストヘッダーを設定します。

  • addExposedHeader メソッド

    許可したオリジンのクライアントに対して公開する必要がある HTTP レスポンスヘッダーを設定します。 アプリケーションで許可する HTTP レスポンスヘッダー名を指定してください。 addExposedHeader メソッドで設定していないレスポンスヘッダーはクライアントに公開されません。

Cookie を使用する場合の注意事項

CORS 環境においてアプリケーションで Cookie を使用する場合、 SameSite 属性に None を明示的に指定する必要があります。設定しない場合、別オリジンへ Cookie を送信できません。 なお、 Cookie の仕様上 SameSite 属性に None を設定する場合は必ず Secure 属性も設定する必要があります( HTTP Cookie の使用 - HTTP | MDN )。

Cookie に SameSite=None が付いた場合は、 Secure 属性も指定することになりました(安全なコンテキストが必要になりました)。

実装方法については、Cookie の設定 を参照してください。

フロントエンドアプリケーション( Vue.js )⚓︎

Web API 呼び出し時の HTTP ヘッダーの設定⚓︎

AlesInfiny Maia では Web API 呼び出しの共通処理用に ./src/api-client/index.ts という設定ファイルを作成する( 参照 )ので、ここで HTTP ヘッダーを設定します。

index.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import axios from 'axios'
import * as apiClient from '@/generated/api-client'

// (中略)

/** axios の共通の設定があればここに定義します。 */
const axiosInstance = axios.create({
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true,
})

const exampleApi = new apiClient.ExampleApi(createConfig(), '', axiosInstance)

export { exampleApi }

withCredentials: true

CORS 環境でのリクエストが Cookie 、認証ヘッダー、 TLS クライアント証明書などの資格情報を使用して行われるべきかを示します。既定値は false なので、 true を明示的に設定します。