nonceで許可されたスクリプトから動的に追加されたスクリプトへ信頼が伝わるstrict-dynamicの概念図

strict-dynamicはnonceまたはhashで許可されたscriptが動的に追加するscriptにも信頼を引き継ぐCSP規則です。対応browserではhost許可listを増やす代わりに、許可したbootstrap scriptとloaderを中心にpolicyを設計できます。CSP Level 3仕様とweb.devのstrict CSP guideを併せて読むと適用範囲が分かります。

nonceで許可する起点とloader

serverはresponseごとに予測困難なnonceを生成し、CSP headerと実行を許可する<script>要素に同じ値を設定します。strict-dynamic対応browserはnonce/hashで許可されたscriptと、それが動的に追加したnon-parser-inserted scriptを信頼できます。この場合、hostやschemeの許可listは主な判断基準ではなく互換性fallbackとして扱われることがあります。

Content-Security-Policy: script-src 'nonce-{responseごとの乱数}' 'strict-dynamic'; object-src 'none'; base-uri 'none'

これは一つのkeywordであらゆる脅威を防ぐという意味ではありません。信頼されたloaderが未検証のuser inputをscript.srcに設定すれば、意図しないcodeを読み込む可能性があります。loaderが参照するURL、manifest、設定も実行境界として確認します。

`'unsafe-eval'`は別の実行許可

strict-dynamicと'unsafe-eval'は別の動作を制御します。script-srcまたはfallbackで'unsafe-eval'を許可すると、eval()、Function() constructor、文字列を受け取るsetTimeout()やsetInterval()などでJavaScriptを動的評価できます。strict-dynamicを加えてもこの許可が自動で解除されるわけではありません。runtimeやbuild成果物が文字列評価に依存する箇所を探し、削除するか必要性を別に判断します。

nonce/hash policyは対応browserで通常のinline scriptを制限しますが、CSPがHTML injection欠陥を修正するわけではありません。responseごとに新しいnonceを作り、同じpageで実行許可を得たcodeにも隠れた秘密だとは扱わないでください。安全なDOM APIとcontext別output encodingで注入原因を直し、信頼済みloaderの入力も検証します。

Report-Onlyで段階導入する

最初から強制するとinline event handlerや動的に追加するscriptが動かなくなることがあります。まずContent-Security-Policy-Report-Onlyで違反を集め、必要な処理をevent listenerへ移し、通常の画面動作とscript読み込み経路を確認します。その後段階的に強制し、reportを継続して確認します。違反報告がないことはXSS欠陥がない証明ではありません。

strict-dynamicを理解しない古いbrowserではfallbackの動作が異なります。web.devは一部旧browser向けにhttps:などのfallbackを検討する方法を説明しますが、そのbrowserでは信頼範囲が広がる場合があります。実際のbrowser versionとapplicationを組み合わせて試してください。

強制前の確認

  • serverはresponseごとに予測困難なnonceを生成しているか。
  • nonceは実行を許可するscriptだけに付き、headerの値と一致するか。
  • 信頼済みloaderが攻撃者制御の値をscript URLにしないか。
  • 'unsafe-eval'の利用箇所を別途確認したか。
  • Report-Only報告と古いbrowserのfallbackを実画面で試したか。

CSPはbrowserのscript実行を制限する防御層の一つです。authentication、authorization、安全なoutput処理、脆弱なcodeの修正、その他のsecurity controlを置き換えません。

参考資料