內容安全政策 (Content Security Policy)

內容安全政策 (CSP) 對於保護您的 Next.js 應用程式免受各種安全威脅非常重要,例如跨站腳本攻擊 (XSS)、點擊劫持和其他程式碼注入攻擊。

透過使用 CSP,開發者可以指定哪些來源是可允許的內容來源,包括腳本、樣式表、圖片、字體、物件、媒體 (音頻、視頻)、iframe 等。

範例

Nonce (一次性隨機數)

Nonce 是一個為一次性使用而建立的唯一隨機字串。它與 CSP 結合使用,可以有選擇地允許某些內聯腳本或樣式執行,繞過嚴格的 CSP 指令。

為什麼使用 nonce?

儘管 CSP 旨在阻止惡意腳本,但在某些合法情況下,內聯腳本是必要的。在這種情況下,nonce 提供了一種方法,如果這些腳本具有正確的 nonce,則允許它們執行。

使用 Middleware 添加 nonce

Middleware 讓您可以在頁面渲染前添加標頭並生成 nonce。

每次查看頁面時,都應該生成一個新的 nonce。這意味著您必須使用動態渲染來添加 nonce

例如:

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 requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-nonce', nonce)
  requestHeaders.set(
    'Content-Security-Policy',
    // 替換換行符和空格
    cspHeader.replace(/\s{2,}/g, ' ').trim()
  )

  return NextResponse.next({
    headers: requestHeaders,
    request: {
      headers: requestHeaders,
    },
  })
}

預設情況下,Middleware 會在所有請求上執行。您可以使用 matcher 來過濾 Middleware 只在特定路徑上執行。

我們建議忽略來自 next/link 的預取請求以及不需要 CSP 標頭的靜態資源。

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:

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}
    />
  )
}

版本歷史

我們建議使用 v13.4.20+ 版本的 Next.js 以正確處理和應用 nonce。

On this page