Craft CMS で CSP 対応をする(最適化編)

前回の記事では、Craft CMS で CSP 対応をする方法について、基本的な流れを確認しました。

今回は、その最適化方法についてまとめてみたいと思います。

これは Craft CMS Advent Calendar 2024 10日目の記事です。

どこが問題になるのか?

CSP プラグイン導入後に利用できる csp ファンクションで、nonce 属性を調整できることがわかりました。

<script nonce="{{ csp('script-src') }}">
  (中略)
</script>

確認しやすいよう script-src ポリシーだけを有効にし、'self''nonce-xxxx' のみを許可するように設定してみると、レスポンスヘッダの Content-Security-Policy は次のようになります。

レスポンスヘッダのサンプル

このとき、同じポリシーに複数の nonce を設定すると、どうなるでしょうか?

<script nonce="{{ csp('script-src') }}">
  (中略)
</script>

(中略)

<script nonce="{{ csp('script-src') }}">
  (中略)
</script>

再度、レスポンスヘッダを確認すると、複数の nonce が設定されていることがわかります。

レスポンスヘッダのサンプル

JS ファイルを用意して、その中で処理をまとめられるなら問題ないことではありますが、プラグインによって追加されるスクリプトやコンテンツ構成の都合で特定のページだけに script タグを記述したい場合が「ない」とは言い切れません。

そこで、グローバル変数 _globals を利用し、テンプレート全体で共通の nonce となるよう調整してみましょう。

グローバル変数 _globals で最適化

_globals は、Craft CMS でグローバル変数を定義するための機能で、Craft 4.5 以降で利用できます。
過去記事で解説していますので、そちらも参考にしてみてください。

レイアウト用テンプレートを調整

{% extends %} タグで読み込むレイアウト用テンプレートファイルの先頭行付近で _globals をセットします。

{% do _globals.set('nonce-scriptSrc', csp('script-src')) %}

このサンプルでは nonce-scriptSrc だけをセットしていますが、必要に応じて他のポリシー向けの記述を加えてください。

利用先のテンプレートを調整

次に、nonce 属性を追加したいテンプレートファイルを調整します。

{% extends '_layout' %}

{% block content %}
  <script nonce="{{ _globals.get('nonce-scriptSrc') }}">
    (中略)
  </script>
{% endblock %}

{% extends '_layout' %} の時点で _globals が読み込まれるため、_globals.get('nonce-scriptSrc') でセットした値を取得できます。

これで、同じポリシーに複数の nonce を設定することなく、テンプレート全体で共通の nonce を利用できるようになりました。

まとめ

Craft CMS で CSP 対応をする際、テンプレート全体で共通の nonce を利用するには次の手順ば必要です。

  • レイアウト用テンプレートで、グローバル変数 _globals に nonce をセットする
  • 利用先のテンプレートで、_globals.get() でグローバル変数を取得する

毎回ではないかもしれませんが、必要になったらぜひ試してみてください。