Content Security Policy Lv2 (CSP2)で ページを他所からフレーム利用させない

ストップ
©いらすとや.

Content Security Policyは基本的にはそれ(CSP)を設定するページで使用する要素を許可するものです。ページでスクリプトを使用するならそのスクリプトやスクリプトの置いてあるホストをscript-srcディレクティブで許可、ページでCSSを使用するならそのCSSやCSSを置いてあるホストをstyle-srcディレクティブで、フレームを使用するならそのフレームの置いてあるホストなどをframe-srcディレクティブで、という風に指定して許可する。

しかし、気色の違うものがあって、フレーム用のframe-ancestorsというディレクティブがそれ。このframe-ancestorsは、それをCSPで指定しているページを他所から使用されることを許可するもの。自身のサイトのページでframe-ancestorsで許可しなければ見ず知らずの他所サイトのiframe内であなたのページを表示されなくなります。ただし、frame-ancestorディレクティブが無い場合は他所の全てのサイトからiframeで読み込まれることを許可しているのと同じになります。他所のウェブサイトからあなたのページをiframeで読み込まれたくなければframe-ancestorディレクティブを指定しましょう。
まぁ、iframeを不許可にしたところでウェブスクレイピングで勝手に使われることはあるかもだけど、この記事とは関係ないのでそれは無視。
今回はそのframe-ancestorsディレクティブを実際に使ってみて動作を確認します。

frame-ancestorsで他所のサイトからiframeで読み込むことを許可する場合

https://denwa.intaa.net/iframe_sample1.phpというページでframe-ancestorsを設定し、他所のサイト「がとらぼ」(intaa.net)を許可する。
Content-Security-Policy: frame-ancestors intaa.net;
そのページを「がとらぼ」(他所のサイト側)でiframe内に読み込む。(↓)は画像ではなく実際にiframeで読み込んでいます。

「がとらぼ」側のiframeタグ:
<iframe src="https://denwa.intaa.net/iframe_sample1.php" width="800" height="480" title="denwa.intaa.net IFRAMEサンプル 1"></iframe>
「がとらぼ」のホストが許可されているので外人の電話オペレータさんたちが写った写真を含むページが表示されている筈。

frame-ancestorsで他所のサイトからiframeで読み込むことを許可しない場合

https://denwa.intaa.net/iframe_sample2.phpというページでframe-ancestorsを設定し、どこからもiframeでの表示を許可しない ' none' を指定する。
Content-Security-Policy: frame-ancestors 'none';
そのページを「がとらぼ」(他所のサイト側)でiframe内に読み込む。(↓)は画像ではなく実際にiframeで読み込みますが許可されていないのでページは表示されていない筈です。

「がとらぼ」側のiframeタグ:
<iframe src="https://denwa.intaa.net/iframe_sample2.php" width="800" height="480" title="denwa.intaa.net IFRAMEサンプル 2"></iframe>
全てのページからiframeで読み込まれることを許可していないので「がとらぼ」のiframeでは接続が拒否されて表示されない。

frame-ancestorsを使って積極的に不許可にしましょう

CSPはウェブサイト側で指定しますが、それをブラウザが解釈して動作し閲覧者を守るセキュリティです。ブラウザが対応していなければ指定した動作にはなりません。frame-ancestorsはIE以外のメジャーブラウザの最近のバージョンは対応します。ブラウザがframe-ancestorsに対応しない場合の動作はframe-ancestors未指定と同じで全て許可です。
殆どのウェブサイトは他所からiframeで読み込まれることを許可する必要がない筈。むしろ悪用を避けるために積極的に禁止すべき。つまりほぼ無条件で frame-ancestors 'none';frame-ancestors 'self'; (自身のホストは許可)を指定するのが良いと思われます。

おまけ: (X-Frame-Optionsヘッダ)
frame-ancestors 'none'は、Content-Security-PolicyヘッダではなくX-Frame-Options: DENY;
frame-ancestors 'self'は、Content-Security-PolicyヘッダではなくX-Frame-Options: SAMEORIGIN;
のようにX-Frame-Optionsヘッダで指定することも可能です。

ホストを許可する場合はX-Frame-Options: ALLOW-FROM host.example.com のように指定することができましたが、これは過去の話です。最近のメジャーブラウザではALLOW-FROMは廃止されているのでCSPのframe-ancestorsを使いましょう。
なお、X-Frame-OptionsはHTMLヘッダではなくHTTPヘッダで指定してください。

Content Security Policy Lv3 (CSP3)でhashにより外部スクリプトを許可する

整理している人
©いらすとや.

前回、Content Security Policy Lv3 (CSP3)でhashによりスクリプトを許可するでインラインスクリプトのハッシュを求めて許可するというのをやったが、そこで少しだけ触れた外部スクリプトをそのまま外部スクリプトとしてハッシュで許可するというのをやってみる。元々は今回nonce(ナンス)を予定していたが思っていたより大変そうなので後回しにした。ごめんなさい。

先に結論だけ書いておきます。ハッシュで外部スクリプトを許可というのはまだとても採用できません。
前回すでに書いていたけど、結局これ。Chromeブラウザでしか使えないのでは話になりません。ハッシュで外部スクリプトを許可したい場合は前回の記事のようにインラインスクリプトから外部スクリプトを読み込むようにしてそのインラインスクリプトを許可してください。

外部スクリプトのハッシュの求め方

インラインスクリプトのハッシュの求め方と大きく違いません。とはいえ、外部スクリプトの内容を読み出す必要があるのでその部分が違います。

https://example.com/dir/script-file.js のハッシュ(sha256)を求める
$ curl --silent "https://example.com/dir/script-file.js" | openssl sha256 -binary | openssl base64
38t2ZdzI/Df3oFOk0g2IWs1/0CwV7NAEnyyMlsdxAVk=      ←求められたハッシュ

CSPヘッダにハッシュを含める (前回と同じ)

CSPヘッダに書くときは、求めたハッシュの前に「sha256-」を付けて「'」で囲む。
'sha256-38t2ZdzI/Df3oFOk0g2IWs1/0CwV7NAEnyyMlsdxAVk='

HTMLヘッダにCSPを書く場合
<meta http-equiv="Content-Security-Policy" content="script-src 'sha256-38t2ZdzI/Df3oFOk0g2IWs1/0CwV7NAEnyyMlsdxAVk=' 'strict-dynamic';">
Nginxの設定でHTTPレスポンスヘッダにCSPを書く場合
add_header Content-Security-Policy "script-src 'sha256-38t2ZdzI/Df3oFOk0g2IWs1/0CwV7NAEnyyMlsdxAVk=' 'strict-dynamic';";

スクリプトタグの変更

インラインスクリプトのハッシュを求めて許可する場合はスクリプトタグはそのまま触らなかったが、外部スクリプトのハッシュを求めて許可する場合は「integrity」を付けて、そこにもハッシュを書く。

HTMLページの中

変更前: <script src="https://example.com/dir/script-file.js"></script>

変更後: <script integrity="sha256-38t2ZdzI/Df3oFOk0g2IWs1/0CwV7NAEnyyMlsdxAVk=" src="https://example.com/dir/script-file.js"></script>

前回のインラインスクリプトをハッシュで許可する場合と僅かな違いはあるものの難しくはありません。ただし、外部スクリプトのURLが変わらないままその中身が変更された場合にはその外部スクリプトがブロックされることになります。許可したいスクリプトが自身の管理であれば変更後にハッシュを再作成してCSPヘッダを更新すればよいのですが、自身の管理でない他所のサイトやCDNにあるようなライブラリなどであれば知らない内にスクリプトが変更されてしまうといつの間にかブロックされていたということがあるかもしれません。(まともなライブラリはバージョン毎にURLが変わるようになっているとは思いますが)

試したのがChromeブラウザ(107.0.5304.62beta)とFirefoxブラウザ(108.0)だけですが、Firefoxではこの外部スクリプトを直接許可というのはまだ機能しませんでした。外部スクリプトをハッシュで許可するというのはCSP Level 3の内容で、CSP3はまだドラフトなので対応してなくても文句は言えません。(2022年12月13日現在)

Up