メインコンテンツまでスキップ

Content Security Policy

Content Security Policy(CSP)は、Next.jsアプリケーションをクロスサイトスクリプティング(XSS)、クリックスジャッキング、その他のコードインジェクション攻撃などのさまざまなセキュリティ脅威から守るために重要です。

CSPを使用することで、開発者はコンテンツソース、スクリプト、スタイルシート、画像、フォント、オブジェクト、メディア(オーディオ、ビデオ)、iframeなどに対して、どのオリジンが許可されるかを指定できます。

Nonce

nonceは、一度の使用のために生成されたユニークなランダム文字列です。CSPと共に使用され、特定のインラインスクリプトやスタイルの実行を選択的に許可し、厳しいCSP指令をバイパスします。

Nonceを使用する理由

CSPは悪意のあるスクリプトをブロックするために設計されていますが、正当なシナリオでインラインスクリプトが必要な場合があります。このような場合、nonceを使用することで正しいnonceを持つスクリプトを実行できるようにする方法を提供します。

Middlewareを使ってNonceを追加する

Middlewareを使用すると、ページがレンダリングされる前にヘッダーを追加したり、nonceを生成したりできます。

ページが表示されるたびに新しいnonceを生成する必要があります。これは、動的レンダリングを使用してnonceを追加する必要があることを意味します。

例:

middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`
// 改行文字とスペースを置換する
const contentSecurityPolicyHeaderValue = cspHeader
.replace(/\s{2,}/g, ' ')
.trim()

const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)

requestHeaders.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
)

const response = NextResponse.next({
request: {
headers: requestHeaders,
},
})
response.headers.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
)

return response
}

デフォルトでは、すべてのリクエストに対してMiddlewareが実行されます。特定のパスでMiddlewareを実行するようにフィルタリングするには、matcherを使用できます。

next/linkからのプリフェッチと、CSPヘッダーが不要な静的アセットの一致を無視することをお勧めします。

middleware.ts
export const config = {
matcher: [
/*
* 次のもので始まるリクエストパスを除いたすべてのパスにマッチします:
* - api(APIルート)
* - _next/static(静的ファイル)
* - _next/image(画像最適化ファイル)
* - favicon.ico(faviconファイル)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}

Nonceの読み取り

Server Componentからheadersを使用してnonceを読み取ることができます:

app/page.tsx
import { headers } from 'next/headers'
import Script from 'next/script'

export default async function Page() {
const nonce = (await headers()).get('x-nonce')

return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}

Nonceを使用しない場合

nonceを必要としないアプリケーションでは、next.config.jsファイルでCSPヘッダーを直接設定できます:

next.config.js
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`

module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: cspHeader.replace(/\n/g, ''),
},
],
},
]
},
}

バージョン履歴

nonceを適切に処理して適用するために、Next.jsのv13.4.20+の使用をお勧めします。