Capítulo 298 de 456

Content Security Policy

Core Idea

Configurar CSP no Next.js protege contra XSS/clickjacking/injeção; pode ser feito via nonce dinâmico gerado no Proxy (renderização dinâmica obrigatória) ou via header estático em next.config.js quando nonce não é necessário.

Key Concepts

  • Nonce: string aleatória única por request usada em script-src/style-src pra liberar scripts inline específicos sem unsafe-inline.
  • Proxy gera o nonce: cria nonce por request, seta em Content-Security-Policy e em header custom x-nonce; precisa rodar em toda rota relevante.
  • Renderização dinâmica obrigatória: nonce só funciona com dynamic rendering, já que é gerado por request; páginas estáticas não têm nonce (build time não tem request).
  • Extração automática pelo Next.js: durante o render, o Next.js lê o header CSP e aplica o nonce automaticamente a scripts/estilos do framework e a <Script nonce={...}>.
  • Sem nonce: header CSP fixo definido em next.config.js via headers(), mais simples mas usa unsafe-inline.
  • connection(): força renderização dinâmica esperando por uma request real, necessário quando se usa nonce.

Code Examples

export function proxy(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
  const isDev = process.env.NODE_ENV === 'development'
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${isDev ? " 'unsafe-eval'" : ''};
    style-src 'self' 'nonce-${nonce}';
    object-src 'none';
    base-uri 'self';
    frame-ancestors 'none';
    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
}
  • O que demonstra: geração de nonce por request e propagação tanto pro request quanto pra response.
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
  const nonce = req.headers['x-nonce']
  return { props: { nonce } }
}
  • O que demonstra: como ler o nonce do header custom via getServerSideProps no Pages Router.

Reference Tables

ModoRenderingCache/CDNComplexidade
Com nonceDinâmico obrigatórioSem cache CDN por padrão, sem ISR/PPRAlta
Sem nonceEstático permitidoCacheável normalmenteBaixa
VersãoMudança
v14.0.0Suporte experimental a SRI (hash-based CSP)
v13.4.20Recomendada pra handling correto de nonce/CSP header

Anti-patterns

  • Usar nonce em página estática: nonce exige request real; build time não tem nonce disponível, então a página precisa ser forçada a dynamic rendering.
  • Combinar nonce com PPR: incompatível, o static shell não tem acesso ao nonce.
  • Deixar 'unsafe-eval' em produção: só necessário em dev (React usa eval pra debug); Next.js/React não usam eval em produção por padrão.
  • Rodar Proxy sem excluir prefetch/estáticos: aumenta custo desnecessário; usar matcher ignorando next-router-prefetch e assets.

Key Takeaways

  1. Nonce = segurança mais forte, mas trava a página em dynamic rendering (perde ISR, PPR, cache CDN).
  2. Sem necessidade de nonce, header CSP fixo em next.config.js é mais simples e mantém cache.
  3. O Next.js aplica o nonce automaticamente a scripts do framework, mas scripts de terceiros ainda precisam do prop nonce manual.
  4. 'unsafe-eval' é exigência só de dev, nunca deveria ir pra produção.

Connects To

  • ch295 Authentication: mesmo mecanismo de Proxy usado pra checagem otimista de sessão.