Subresource Integrity (SRI)
ビルド成果物の HTML に SHA-384 ベースの integrity 属性を自動注入する仕組み。
なぜ必要か
- CDN や中間プロキシで JS/CSS が改ざんされた際、ブラウザがハッシュ不一致を検出してロードを拒否する。
- npm dependency の supply-chain 攻撃で bundle が差し替えられても、デプロイ時のハッシュと一致しない限り実行されない。
- BFF と分離した SPA を Cloudflare Pages から配信しているため、Pages 側 attack surface を 1 段下げる。
実装
共通ロジック
web/scripts/sri.mjs — ゼロ依存 Node CLI。
- 引数に
distディレクトリを取り、*.htmlを再帰走査 <script src="...">/<link rel="stylesheet|modulepreload|preload" href="...">のうち ローカル相対 / 同オリジン絶対パスだけ対象- 既に
integrity=が付いている tag はスキップ(冪等) crossorigin="anonymous"も併せて付与
Vite Portal(admin / owner / marketing)
package.json の build 末尾に連結:
"build": "tsc -b && vite build && node ../../scripts/sri.mjs ./dist"
Astro Home
web/home/src/integrations/sri.mjs という薄い integration を作り、
astro:build:done フックで web/scripts/sri.mjs を呼ぶ。
検証
ビルド後、出力 HTML の script/link に integrity="sha384-..." が付いていれば成功。
grep -o 'integrity="sha384-[^"]*"' web/portal/admin/dist/index.html | head
ブラウザの DevTools → Network → Response Headers で対象アセットが 200 で返り、
Console に Failed to find a valid digest in the 'integrity' attribute が出ていないことを確認。
制約
- 外部ドメイン(
https://...)は対象外。crossorigin SRI は CORS で失敗するケースが多いため。 data:/blob:URL も対象外。- HTML を再生成すると SRI 再注入が必要。Astro/Vite は build hash を変える前提なので問題なし。
トラブルシュート
SRI mismatch エラー
- ブラウザに古い HTML がキャッシュされている場合、新しい bundle と integrity が合わない。
- 対策: Cloudflare Pages の deploy で HTML の Cache-Control を
no-cacheにする(既存設定)。
inline <script> には付かない
- src 属性が無い
<script>{...}</script>は対象外(CSP のunsafe-inlineかnonceで対応する)。