跳到主要内容

内容安全策略

内容安全策略(Content Security Policy,CSP)对于保护您的 Next.js 应用免受各种安全威胁, 如跨站脚本(XSS)、点击劫持和其他代码注入攻击非常重要。

通过使用 CSP,开发人员可以指定哪些来源对于 内容源、脚本、样式表、图像、字体、对象、媒体(音频、视频)、iframes 等是允许的。

Nonces​

nonce 是为一次性使用而创建的唯一的随机字符串。它与 CSP 结合使用,以有选择地允许某些内联脚本或样式执行,绕过严格的 CSP 指令。

为什么使用 nonce?​

尽管 CSP 旨在阻止恶意脚本,但有些情况下需要内联脚本是合法的。在这种情况下,nonce 为这些脚本提供了一种执行的方式,如果它们具有正确的 nonce,则允许执行。

使用中间件添加 nonce​

中间件使您能够在页面渲染之前添加标头并生成 nonces。

每次查看页面时,都应生成一个新的 nonce。这意味着您必须使用动态渲染来添加 nonces。

例如:

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';
block-all-mixed-content;
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
}

默认情况下,中间件在所有请求上运行。您可以使用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​

现在,您可以使用 headers 从 Server Component 中读取 nonce:

app/page.tsx
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}
/>
)
}

版本历史​

我们建议使用 Next.js 的 v13.4.20+ 版本以正确处理和应用 nonces。