@k8ordo/server

CSPを設定する

Content-Security-Policyを設定すると、コンテンツに紛れ込んだスクリプトを動かさずに済みます。このモードでは、フレームワークが応答ごとに新しいnonceを作るので、それを名指すポリシーをguard.tsでヘッダーに書きます。

このページの内容

フレームワークが署名するもの

フレームワークはポリシーを決めません。自分がHTMLに書くインラインスクリプトに署名するだけで、それを許すポリシーはアプリが書きます。

フレームワークが書くインラインスクリプトは、hydrationのためにHTMLへ埋め込むペイロードと、Reactのスクリプトです。

アプリが自分で書くインラインスクリプトも、同じようにポリシーで許します。@k8ordo/color-schemeが描画の前に色を決めるスクリプトも、その1つです。

guard.tsでポリシーを書く

フレームワークは応答ごとにnonceを作り、自分のインラインスクリプトとモジュールのスクリプトに付けます。@k8ordo/server/runtimeのnonce()がその値を返すので、ルートのguard.tsでポリシーに書き込みます。

src/routes/guard.ts
import { nonce, responseHeaders } from '@k8ordo/server/runtime';

export default function guard() {
  const policy = [
    `script-src 'nonce-${nonce()}' 'strict-dynamic'`,
    "object-src 'none'",
    "base-uri 'none'",
  ].join('; ');
  responseHeaders().set('content-security-policy', policy);
}

ルートのguard.tsは、redirect.tsを除くすべての答えの前に走ります。そのため、ページにもペイロードにも、どこにも当たらなかったURLへの404にも、同じポリシーが付きます。

モジュールのスクリプトにもnonceが付くので、'strict-dynamic'の下では、そこから残りのクライアントのコードが読み込まれます。

アプリのインラインスクリプトに署名する

アプリ自身のインラインスクリプトには、同じnonceを付けます。@k8ordo/color-schemeなら、ルートレイアウトでColorSchemeProviderにnonce={nonce()}を渡します。

src/routes/layout.tsx
import { ColorSchemeProvider } from '@k8ordo/color-scheme';
import { nonce } from '@k8ordo/server/runtime';
import type { ReactNode } from 'react';

export default function RootLayout({
  children,
}: {
  children: ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ColorSchemeProvider nonce={nonce()}>
          {children}
        </ColorSchemeProvider>
      </body>
    </html>
  );
}

nonce()は、リクエストに答えている間ならどこで呼んでも同じ値を返します。描画の中も同じです。スクリプトに署名することは、応答を書くことではないからです。

リクエストの外、たとえばモジュールのトップレベルで呼ぶと、例外を投げます。

nonceの付いた答えをキャッシュしない

nonceは、答えのたびに新しいことで意味を持ちます。CDNなどの共有キャッシュに置くと同じnonceが何度も配られ、攻撃者が前もって知ったnonceを付けて、スクリプトを差し込めてしまいます。nonceを含む答えは、共有キャッシュに置かないでください。

k8ordo

Baselineに入った機能を、制限なく使うReactのライブラリ群。

© 2026 k8o — MIT License

組版:Noto Sans JP / M PLUS 2