Skip to content

CSP対応とimportmapの運用指針(nonce付与とHTML構成) #1

@mogera551

Description

@mogera551

背景

Structiveでは <script type="importmap"> を使う構成を取っているが、本番環境で Content-Security-Policy (CSP) を適用する際に、インラインスクリプトがブロックされる可能性がある。

importmap は仕様上、外部ファイル(src指定)には対応しておらず、HTML内にインラインで埋め込む必要があるため、CSP制限との両立が課題となる。


方針(CSP対応方針)

  • importmap は HTML内にインラインで記述する
  • ✅ CSP制限を回避するために、nonce を付与して許可する
  • nonce はリクエストごとに ランダムに生成される一時トークン
  • ✅ 同じ nonce を HTMLスクリプトタグの nonce 属性と CSPヘッダー の両方に含める必要がある

実装例(Express.js + EJS)

✅ サーバー側:nonce を生成し、CSPに埋め込む

const crypto = require("crypto");

app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString("base64");
  res.locals.nonce = nonce;

  res.setHeader("Content-Security-Policy",
    `script-src 'self' 'nonce-${nonce}'`
  );

  next();
});

✅ テンプレート側(EJSなど)

<!-- importmapにnonceを適用 -->
<script type="importmap" nonce="<%= nonce %>">
{
  "imports": {
    "structive": "https://cdn.jsdelivr.net/npm/structive@0.1.0/dist/structive.min.js"
  }
}
</script>

<!-- モジュール実行にも同じnonceを適用 -->
<script type="module" nonce="<%= nonce %>">
  import { defineComponents } from "structive";
  defineComponents({ "my-comp": "myComponent" });
</script>

注意点

  • ❌ nonce を固定値にするのはNG(XSS耐性が失われる)
  • ❌ script-src 'unsafe-inline' に頼る構成は避ける
  • ✅ nonce はリクエストごとに動的に発行されるべき
  • ✅ テンプレートとヘッダーの両方で同じnonceを使用すること

今後の検討事項

  • Vite / Astro / 静的HTML生成環境での対応パターン
  • importmapをテンプレートエンジンで自動埋め込みできる仕組み
  • GitHub Pagesでの限定的代替(現状はCSP設定不可)

備考

このIssueはStructiveの本番運用に向けたセキュリティ強化対応の備忘録です。
必要に応じてWikiや docs/security.md に昇格予定。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions