SecureHtmlRendererを理解してAdobe Commerce / Magento Open SourceでContent Security Policy対応を高めるには

2024年6月のCosmicSting(CVE-2024-34102)公表以降、Content Security Policy(CSP)の重要性に対する認識が高まってきています。
Adobe Commerce / Magento Open SourceにおけるCSP対応はバージョン2.3.5で導入されたもので、すでに4年以上経過していますが、CosmicStingをきっかけにCSPを有効化するサイトも増えているようです。
既に2.4.7系や2.4.6-p6、2.4.5-p8、2.4.4-p9以降のバージョンではCSPの動作モードが変更され、チェックアウト画面では許可されていないスクリプトは動作しないようになりました。

CSPに関するポリシーや動作モードについては以前の記事で解説していますが、今回はcsp_whitelist.xmlの問題点とインラインスクリプトなどの対応方法について解説をしていきたいと思います。

csp_whitelist.xmlが抱える問題点

まず最初はcsp_whitelist.xmlが抱える問題点についてです。

このXMLファイルは、CSPにおけるホワイトリストを定義するためのものです。
ホワイトリストなので、システム管理者があらかじめ把握しているものだけが許可されることになるわけですが、その対象は

  • 外部サイトから配信されるスクリプト

が主な対象です。

  • HTML本文中に記述されるJavaScript
  • HTML本文中に記述されるスタイルシート定義

は配信元URLがないため許可されないことがあります。
これらについては「ハッシュ値をあらかじめ計算したうえで、csp_whitelist.xmlに追記」することで対応が可能となっています。
手間ではありますが、ハッシュ値さえわかればcsp_whitelist.xmlに記述すること自体は可能です。

csp_whitelist.xmlで定義ができないものとは

csp_whitelist.xmlでどうやっても定義ができないものとしては、以下の2つが挙げられます。

  • イベントリスナー
  • 内容が動的に変わるインラインスクリプト

なぜ定義できないのかというと、

  • イベントリスナー:csp_whitelist.xmlで記述ができない(想定されていない)
  • 内容が動的に変わるスクリプト: csp_whitelist.xmlに一応定義可能だが、ハッシュ値が一定ではないので定義の意味がない

という理由があるからです。
そのためこれらを許可するためには実装面で工夫をする必要が出てきます。

Magento\Framework\View\Helper\SecureHtmlRendererを利用したCSP対策

csp_whitelist.xmlで定義しきれないスクリプトについては、Magentoフレームワークは「Magento\Framework\View\Helper\SecureHtmlRenderer」を用意しています。
このくらすは、最近のバージョンでは「Magento\Framework\View\TemplateEngine\Php」に対するViewModelとして以下のように宣言されています。

    <type name="Magento\Framework\View\TemplateEngine\Php">
        <arguments>
            <argument name="blockVariables" xsi:type="array">
                <item name="secureRenderer" xsi:type="object">Magento\Framework\View\Helper\SecureHtmlRenderer\Proxy</item>
                <item name="escaper" xsi:type="object">Magento\Framework\Escaper</item>
                <item name="localeFormatter" xsi:type="object">Magento\Framework\Locale\LocaleFormatter</item>
            </argument>
        </arguments>
    </type>

どのphtmlテンプレート上からでも暗黙的に呼び出すことができるので、よほど独特なカスタマイズをしていない限りは大きな変更なく使い始めることができます。
このクラスには

  • renderTag
  • renderEventListener
  • renderEventListenerAsTag
  • renderStyleAsTag

という4つのメソッドが用意されています。
メソッド名からなんとなく用途がわかるものばかりですね。基本はrenderTagを用いてスクリプトを描画します。

SecureHtmlRendererが用意するメソッドでタグ描画するとどうなるか

SecureHtmlRendererが用意しているメソッドを利用して出力されたタグについては、属性に「nonce」がハッシュ値付きで追加されます。
こうすることでCSPのポリシーに抵触することなく、インラインスクリプトの利用ができます。

なお、前述の4つのメソッドのうち、

  • renderEventListenerAsTag
  • renderStyleAsTag

はrenderTagと基本的には同じ挙動を示しますが特有の部分が存在します。

renderStyleAsTag

このメソッドを使用して描画を行った場合、CSS定義がJavaScript化されるという特徴があります。
styleタグにnonceをつけるのではない、というところが独特です。

renderEventListenerAsTag

SecureHtmlRendererが用意しているメソッドを利用して出力されたタグについては、属性に「nonce」がハッシュ値付きで追加されます。
こうすることでCSPのポリシーに抵触することなく、インラインスクリプトの利用ができます。

なお、前述の4つのメソッドのうち、

  • renderEventListenerAsTag
  • renderStyleAsTag

はrenderTagと基本的には同じ挙動を示しますが特有の部分が存在します。

特にrenderStyleAsTagの場合はCSS定義がJavaScript化されるという特徴があります。
renderEventListenerについては、scriptタグを使用しない関係上、CSPヘッダーに対象のイベントリスナーを許可するための情報を動的に追加します。
この場合、HTTPヘッダーに含まれるCSPヘッダーの値が肥大化するので、HTTPヘッダーサイズには十分注意しましょう。CDNやキャッシュサーバーの設定によっては、オリジンサーバーからのレスポンスが異常なものとして扱われてしまい、正常にクライアントへ送信されない場合がでてきてしまいます。

次回予告

次回は以下の内容を取り上げる予定です、

  • CMSブロックに直書きされるJavaScriptへの対策
  • GTMなどのタグマネージャー運用との折り合い

CSPの運用は今後必須になるため、きちんと押さえていくようにしましょう。