コンテンツにスキップ

クリックジャッキング⚓︎

クリックジャッキングとは⚓︎

安全なウェブサイトの作り方 - 1.9 クリックジャッキング | 情報セキュリティ | IPA 独立行政法人 情報処理推進機構 よりクリックジャッキングの定義を以下に引用します。

ウェブサイトの中には、ログイン機能を設け、ログインしている利用者のみが使用可能な機能を提供しているものがあります。該当する機能がマウス操作のみで使用可能な場合、細工された外部サイトを閲覧し操作することにより、利用者が誤操作し、意図しない機能を実行させられる可能性があります。このような問題を「クリックジャッキングの脆弱性」と呼び、問題を悪用した攻撃を、「クリックジャッキング攻撃」と呼びます。

AlesInfiny Maia OSS Edition でのクリックジャッキング対策⚓︎

AlesInfiny Maia OSS Edition(以降『AlesInfiny Maia』)では、クリックジャッキング対策としてフレーム内表示を原則としてすべて禁止します。

具体的には、以下の方針を採用します。

  • 主要ブラウザー向けの対策として
    • Content-Security-Policy ヘッダーの frame-ancestors 'none' を設定
  • レガシーブラウザー向けの後方互換対策として
    • X-Frame-Options: DENY を併せて設定

これにより、 AlesInfiny Maia ではデフォルトで一切の埋め込み表示を許可しないセキュアな構成を実現します。 以降、各設定項目について説明します。

Content-Security-Policy : frame-ancestors⚓︎

HTTP レスポンスヘッダーに対して Content-Security-Policy ヘッダーフィールド frame-ancestors ディレクティブ を出力します。

frame-ancestors は、どのオリジンから当該コンテンツを <frame> 要素や <iframe> 要素、 <embed> 要素、 <object> 要素で読み込めるかを指定するためのディレクティブです。 以下のような特徴があります。

  • 複数オリジンの指定が可能
  • ワイルドカード指定が可能(※ AlesInfiny Maia では禁止)
  • 主要ブラウザーで広くサポート

以下に示す frame-ancestors の指定内容により、フレーム内表示の許可範囲が異なります。

設定値 表示できる範囲
frame-ancestors 'none'; すべてのオリジンからのフレーム内の表示を禁止する
frame-ancestors 'self'; 同一オリジンからのフレーム内の表示のみを許可する
frame-ancestors https://example.com; 指定したオリジンからのフレーム内の表示のみを許可する
frame-ancestors https://example.com https://sub.example.com; 複数の指定したオリジンからのフレーム内の表示を許可する

AlesInfiny Maia では、方針のとおり frame-ancestors 'none'; を設定します。

X-Frame-Options⚓︎

HTTP レスポンスヘッダーに対して X-Frame-Options ヘッダーフィールド を出力します。 これにより、他ドメインのサイトからの <frame> 要素や <iframe> 要素、 <embed> 要素、 <object> 要素による読み込みを制限します。

以下に示す指定内容により、フレーム内表示の許可範囲が異なります。

設定値 表示できる範囲
DENY すべてのドメインからのフレーム内の表示を禁止する
SAMEORIGIN 同一オリジンからのフレーム内の表示のみを許可する
ALLOW-FROM (非推奨) 指定したオリジンからのフレーム内の表示のみを許可する

かつてはクリックジャッキング対策の主流でしたが、以下の制約があります。

  • 指定可能なオリジンが限定的
  • ALLOW-FROM は主要なモダンブラウザーで互換性がない

このため、 X-Frame-Options はレガシーブラウザー向けの補助的な対策として位置づけます。

こちら に記載のとおり、より包括的な設定をする場合には Content-Security-Policy : frame-ancestors を使用するよう推奨されています。

このヘッダーで提供されるオプションよりも包括的な設定については、Content-Security-Policy ヘッダーの frame-ancestors ディレクティブを参照してください。

AlesInfiny Maia では、方針のとおり X-Frame-Options: DENY を設定します。

ブラウザーにおけるヘッダーの優先順位⚓︎

Content-Security-Policyframe-ancestorsX-Frame-Options の両方が設定されている場合、以下のような挙動になります。

  • モダンブラウザー
    • frame-ancestors を優先し、 X-Frame-Options を無視
  • frame-ancestors 非対応のレガシーブラウザー
    • X-Frame-Options にフォールバック

アプリケーションの設定⚓︎

AlesInfiny Maia では、 Spring Security を利用して X-Frame-Options および frame-ancestors を設定します。 具体的には、 Spring Security が提供する セキュリティ HTTP レスポンスヘッダー により構成します。

実装例を以下に示します。 本設定では、前述のとおり後方互換性のために X-Frame-Options を設定しつつ、実際のフレーム制御は frame-ancestors によって行う構成としています。

SecurityFilterChain の HTTP レスポンスヘッダー設定例
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
package com.dressca.web.consumer.security;

import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

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

  @Value("${cors.allowed.origins:}")
  private String[] allowedOrigins;

  /**
   * 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(cors -> cors.configurationSource(request -> {
          CorsConfiguration conf = new CorsConfiguration();
          conf.setAllowCredentials(true);
          conf.setAllowedOrigins(Arrays.asList(allowedOrigins));
          conf.setAllowedMethods(List.of("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"));
          conf.setAllowedHeaders(List.of("*"));
          // 注文情報の確定にLocationを利用するため公開ヘッダーとして設定
          conf.addExposedHeader("Location");
          return conf;
        }));

    return http.build();
  }
}
設定 内容
frameOptions().deny() X-Frame-Options: DENY を設定(レガシーブラウザー向け)
csp.policyDirectives("frame-ancestors 'none';") frame-ancestors 'none' を設定(主要なモダンブラウザー向け)

制限変更の場合⚓︎

前述のとおり、 AlesInfiny Maia ではクリックジャッキング対策としてデフォルトでフレーム内表示をすべて禁止する方針を採用しています。 ただし、業務要件上正当な理由で <iframe> 要素等の埋め込みが必要となる場合に限り、以下のような制限の変更を検討します。

同一オリジン内での埋め込みが必要な場合⚓︎

  • 同一オリジン内において、複数の Web リソースをフレーム要素等で構成する設計が採用されている場合
  • 同一オリジン内の別パスに配置されたコンテンツを、フレーム要素等を用いて表示する必要がある場合

このような場合には、同一オリジンからの埋め込みのみを許可します。

ヘッダー 設定値
Content-Security-Policy frame-ancestors 'self';
X-Frame-Options SAMEORIGIN

特定の外部オリジンからの埋め込みが必要な場合⚓︎

  • 信頼境界内にある特定の外部オリジンから、フレーム要素等を用いた表示を許可する必要がある場合
  • 信頼された別オリジンの Web リソースと、画面統合する設計が採用されている場合

このような場合には、許可するオリジンを明示的に列挙します。

ヘッダー 設定値
Content-Security-Policy frame-ancestors https://example.com;
X-Frame-Options DENY

X-Frame-Options : ALLOW-FROM はブラウザー互換性の問題があるため使用せず、 X-Frame-Options : DENY に設定します。 これにより、 Content-Security-Policy に対応のブラウザーは frame-ancestors により埋め込みが許可され、非対応のブラウザーには埋め込みを許さないようになります。 なお、ワイルドカードによる埋め込み許可は禁止します。