內容安全策略
內容安全策略 (CSP) 對於保護您的 Next.js 應用程式免受各種安全威脅(例如跨網站指令碼 (XSS)、點擊劫持和其他程式碼注入攻擊)至關重要。
透過使用 CSP,開發人員可以指定哪些來源允許作為內容來源、指令碼、樣式表、圖片、字型、物件、媒體(音訊、影片)、iframe 等。
範例
隨機數
一個 隨機數 是一個獨一無二的、僅供一次性使用的隨機字串。它與 CSP 搭配使用,可以選擇性地允許某些內嵌指令碼或樣式執行,繞過嚴格的 CSP 指令。
為什麼要使用隨機數?
即使 CSP 的設計目的是為了阻擋惡意指令碼,但在某些合法情況下,內嵌指令碼是必要的。在這種情況下,如果內嵌指令碼具有正確的隨機數,則隨機數可以提供一種允許這些指令碼執行的方式。
使用中介軟體新增隨機數
中介軟體 讓您可以在頁面渲染之前新增標頭並產生隨機數。
每次瀏覽頁面時,都應該產生一個新的隨機數。這表示您必須使用動態渲染來新增隨機數。
例如:
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;
`
// 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
}
根據預設,中介軟體會在所有請求上執行。您可以使用 matcher
過濾要在特定路徑上執行的中介軟體。
我們建議忽略匹配的預取請求(來自 next/link
)和不需要 CSP 標頭的靜態資源。
middleware.ts
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' },
],
},
],
}
讀取隨機數 從 伺服器組件 讀取隨機數。
app/page.tsximport { 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://127.0.0.1/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
無 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://127.0.0.1/gtag/js"
strategy="afterInteractive"
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, ''),
},
],
},
]
},
}
版本歷史記錄
這有幫助嗎?