Content Security Policy
Content Security Policy (CSP) は、クロスサイトスクリプティング(XSS)、クリックジャック、その他の code 挿入攻撃など、様々なセキュリティ脅威からあなたの Next.js アプリケーションを保護するために重要です。
CSP を使用することで、開発者はコンテンツソース、scripts、スタイルシート、画像、フォント、Object、メディア(オーディオ、ビデオ)、iframe などに許可可能な原点を指定することができます。
Examples
Nonces
nonce とは、一回限りの使用のために作られたユニークでランダムな string の文字列です。それは、厳格な CSP ディレクティブを迂回して、一部のインラインの scripts や styles を選択的に実行するために、CSP と組み合わせて使用されます。
なぜ nonce を使用するのですか?
CSP は悪意のある scripts をブロックするように設計されていますが、インライン scripts が必要な正当なシナリオも存在します。そのような場合、nonces はそれらの scripts が正しい nonce を持っていれば実行を許可する方法を提供します。
Middleware を使用して nonce を追加する
Middlewareは、ページがレンダリングされる前に、headers を追加し、nonces を生成することを可能にします。
ページが表示されるたびに、新しい nonce が生成されるべきです。これは、あなたがnonce を追加するために dynamic レンダリングを使用しなければならないことを意味します。
For example:
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';
block-all-mixed-content;
upgrade-insecure-requests;
`
// Replace newline characters and spaces
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
}
import { NextResponse } from 'next/server'
export function middleware(request) {
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';
block-all-mixed-content;
upgrade-insecure-requests;
`
// Replace newline characters and spaces
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
}
default では、Middleware はすべての Request で実行されます。matcher
を使用して、特定の paths で Middleware を実行するようにフィルタリングすることができます。
私たちはマッチングのプリフェッチ(next/link
から)と、CSP header が不要な静的アセットを無視することをお勧めします。
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
nonce の読み取り
あなたは今、headers
を使ってServer Componentから nonce を読むことができます。
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = headers().get('x-nonce')
return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = headers().get('x-nonce')
return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
Without Nonces
require を必要としないアプリケーションの場合、next.config.js
ファイルに直接 CSP header を設定することができます。
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';
block-all-mixed-content;
upgrade-insecure-requests;
`
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: cspHeader.replace(/\n/g, ''),
},
],
},
]
},
}
Version History
我々は、非スと正しく処理し、適用するために、Next.js のv13.4.20+
を使用することをお勧めします。