Skip to main content

内容安全策略

内容安全策略(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

现在,您可以使用 headersServer 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。