X-Frame-Optionsが非推奨に!
以前は、クリックジャッキング攻撃の対策にはサーバーのHTTPリクエストヘッダーにX-Frame-Optionsを設定するのが主流でした。
しかし、その設定は推奨されていないセキュリティ対策になってきているかもしれません。
特にログイン後に個人情報や重要データを扱うWebアプリケーションであれば、iframeによる不正な読み込み(クリックジャッキング攻撃)にさらされるリスクがあります。
この記事では、そんな攻撃を防ぐために知っておくべき「frame-ancestors」という新しい手法と、旧来の X-Frame-Options との違い、私が実際に設定した方法も含めて、初心者向けにやさしく解説します。
あれ、非推奨だ・・・
私は先日、JavaでWebアプリケーション開発をSpring Securityで構築していました。
クリックジャッキング対策には、セキュリティ設定の中に X-Frame-Options: DENY
を入れておけば大丈夫だろうと思っていました。
後になってMDNのX-Frame-Optionsのページを見ていたら、X-Frame-Optionsが非推奨になっていることを知って、少し冷や汗をかきました。

ものすごい警告されてる・・・
クリックジャッキングとは?
はじめにクリックジャッキング攻撃とは、悪意あるサイトが自分のWebページを透明なiframeで読み込み、ユーザーに気づかれないようにクリックさせる攻撃です。
たとえば、ユーザーが「ゲームスタート」ボタンを押したつもりが、実際にはログイン中の銀行アプリの「送金」ボタンを押していたなんて被害が現実に起こり得ます。
これは「ゲームスタート」ボタンの上に透明な「送金」ボタンを重ねられていたからです。
この場合は「送金」ボタン側で知らないサイトに埋め込めないように今回の設定を行います。
対策が必須なシーン
特に気をつけるのは、重要な操作を行いたいときです。例えば、
- ログイン後に個人情報を表示・操作するページ
- 設定変更や支払い処理が行える管理画面
- SNSの投稿や削除など、ユーザーの意思が問われる操作
X-Frame-Optionsとは?
かつて主流だったX-Frame-Options
は、iframeによる読み込みを制限するためのヘッダです。
設定値 | 意味 |
---|---|
DENY | すべてのiframe読み込みを拒否 |
SAMEORIGIN | 同一オリジンのみ許可 |
ALLOW-FROM | 特定のURLのみ許可(※非推奨、主要ブラウザ未対応) |
事前知識①:HTTPレスポンスヘッダとは?
HTTPレスポンスヘッダとは、Webサーバーがブラウザへ返す応答の中に含まれる情報で、
「どのように表示するか」や「どの外部リソースを許可するか」などを制御できます。
iframe制御に使われる X-Frame-Options
や Content-Security-Policy
もこの一種です。
画面を開くときはWebサーバにGETでリクエストを送信しますが、そのレスポンスのヘッダ情報に設定されているのがHTTPレスポンスヘッダです。
事前知識②:オリジンってなに?
Webセキュリティでよく出てくるオリジン(origin)とは、
- スキーム(http/https)
- ホスト名(ドメイン)
- ポート番号(省略時は自動補完)
この3つのセットのことです。
たとえばhttps://masa-enjoy.com:443
とhttp://masa-enjoy.com:80
は、見た目が似ていても別オリジンです。
iframeの制御では、どのオリジンから読み込まれたかが判断基準になるので、重要な概念です。
X-Frame-Optionsはなぜ今は使うべきではないのか?
- 複数ドメインの許可ができない(柔軟性がない)
ALLOW-FROM
は 非標準で、Chromeなどでは無視される可能性がある- W3Cの正式仕様でもない
という理由があるからです。
Content-Security-Policy(CSP)とframe-ancestors
そこで登場したのが、より強力で柔軟なContent-Security-Policy(CSP)の中の frame-ancestors
ディレクティブです。
これは、iframeで読み込まれる側(=被害者側)が自分で「誰に読み込ませるか」を定義できるセキュリティ設定です。
設定例と設定値
Content-Security-Policy: frame-ancestors 'none';
frame-ancestorsの右側に記載する設定値はこちらです。
設定 | 意味 |
---|---|
'none' | すべてのiframe表示を拒否(最も安全) |
'self' | 同一オリジンのみ許可 |
https://trusted.com | 指定したドメインのみ許可 |
'self' https://trusted.com | 複数指定も可能 |
X-Frame-Optionsとframe-ancestorsのマッピング
移行する際などX-Frame-OptionsをContent-Security-Policyのframe-ancestorsに変更したい場合は以下のように書き換えます。
X-Frame-Options | frame-ancestors(代替) |
---|---|
DENY | 'none' |
SAMEORIGIN | 'self' |
ALLOW-FROM uri | uri (複数指定可能) |
つまりX-Frame-OptionsでDENYを設定している場合はframe-ancestorsにnoneを設定しましょう。
Spring Securityでの設定方法(Java)
Spring Security 6.4では、デフォルトで X-Frame-Options: DENY
が設定されます。
そのため何も設定しないとiframeが使用できません。
もしiframeを使いたい場合はframe-ancestorsを設定しましょう。
このときframe-ancestors
を指定すると、X-Frame-Options
は無視されます。
そのためframe-ancestorsにselfを設定している場合にX-Frame-OptionsにDENYを設定していても同一オリジンからの埋め込みは可能になるのでどちらも設定する場合は注意が必要です。
.headers(headers -> headers.contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'none';") ) )
Content-Security-Policyを使うことで明示的にframe-ancestorsを設定でき、より正確で柔軟な制御が可能になったと感じています。
どんな設定を選ぶべき?
私の経験から言うと、
- iframeをまったく使わないサイト は
'none'
- 自分のドメインからの埋め込みだけ許可したい は
'self'
- 信頼できる外部のサイトからも読み込ませたい場合は
'self' https://masa-enjoy.com
という考え方で運用しています。
最後のまとめ
- X-Frame-Optionsは非推奨、現代のブラウザではCSPを使う
- iframeを使った攻撃「クリックジャッキング」には frame-ancestorsでの防御が必須
'none'
を基本に、用途に応じて'self'
やドメイン指定を使い分ける- Spring Securityでも簡単に導入できる
- 設定の意図を理解した上で制御することが大切
セキュリティ設定って、「知ってるつもり」が一番危険だと実感しました。
この記事が、あなたのWebアプリを少しでも安全にするきっかけになれば嬉しいです。