CSPを設定する
Content-Security-Policyを設定すると、コンテンツに紛れ込んだスクリプトを動かさずに済みます。このモードでは、framework()のcspにポリシーを渡すと、ビルドが各ページの<meta>に書き込みます。
このページの内容
フレームワークが署名するもの
フレームワークはポリシーを決めません。自分がHTMLに書くインラインスクリプトに署名するだけで、それを許すポリシーはアプリが書きます。
フレームワークが書くインラインスクリプトは、hydrationのためにHTMLへ埋め込むペイロードと、Reactのスクリプトです。
アプリが自分で書くインラインスクリプトも、同じようにポリシーで許します。@k8ordo/color-schemeが描画の前に色を決めるスクリプトも、その1つです。
cspオプションにポリシーを渡す
静的なファイルは誰が読んでも同じ中身なので、リクエストごとに変えるnonceは使えません。そこでビルドは、フレームワークのインラインスクリプトをハッシュで名指しします。
vite.config.tsimport { colorSchemeScriptHash } from '@k8ordo/color-scheme';
import { framework } from '@k8ordo/static';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
framework({
csp: {
'script-src': ["'self'", await colorSchemeScriptHash()],
'object-src': ["'none'"],
'base-uri': ["'none'"],
},
}),
],
});cspには、ディレクティブごとにソースの配列を渡します。各ページの<head>の先頭に<meta http-equiv="Content-Security-Policy">が書かれ、そのページのスクリプトのハッシュが足されます。
ハッシュを足す先はscript-srcです。default-srcだけを書いたときは、そこからscript-srcを作って足します。script-src-elemを書いたときは、そちらにも足します。どれも書いていなければスクリプトは制限されないので、ハッシュも足しません。
cspを渡さなければ、ポリシーは書かれません。
アプリのインラインスクリプトを許す
アプリ自身のインラインスクリプトは、そのハッシュをポリシーに書いて許します。@k8ordo/color-schemeなら、colorSchemeScriptHash()がハッシュを返します。
ColorSchemeProviderにdefaultPreferenceを渡しているときは、colorSchemeScriptHash()にも同じ値を渡します。スクリプトの中身が変わり、ハッシュも変わるからです。
ハッシュを書いていないインラインスクリプトは、すべて拒まれます。コンテンツから紛れ込んだスクリプトが動かないのは、このためです。
cspに書けないもの
フレームワークのモジュールのスクリプトは、ファイルでは署名できないので、出どころの'self'で許されます。'strict-dynamic'があると'self'が無視されるので、それを含むポリシーは拒みます。
frame-ancestorsとreport-uri、sandboxも拒みます。<meta>に書いても効かないディレクティブなので、ホスティングのヘッダーで設定してください。
落とし穴
拒むのはframework()を呼んだ時点です。the "csp" option cannot go into a page's <meta> as it is:に続けて、問題を1行ずつ挙げた例外を投げます。