跳至內容

內容安全策略

內容安全策略 (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' },
      ],
    },
  ],
}

讀取 nonce 值

您現在可以使用 headers伺服器組件 讀取 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 值

對於不需要 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, ''),
          },
        ],
      },
    ]
  },
}

版本歷史記錄

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