Next.js: Fortalecimento de Segurança & Migração de Versão
Última atualização: 2026-08-26
Segurança e migração de versão são as duas linhas de defesa para aplicações em nível de produção — a primeira previne ataques externos, enquanto a segunda garante que a stack tecnológica permaneça livre de dívidas.
1. O Que Você Vai Aprender
- Implementar proteção XSS, tokens CSRF e cabeçalhos de segurança HTTP (headers do next.config.js)
- Prevenir vazamentos de variáveis de ambiente (convenção NEXT_PUBLIC_* / .env*.local no .gitignore)
- Usar npm audit e Snyk para realizar auditorias de segurança de dependências
- Dominar a migração v14→v15: Tratamento assíncrono de params, cookies e headers
- Compreender a migração v15→v16: proxy.ts substituindo Middleware, Turbopack como padrão e migração do "use cache"
2. Uma História Real de um Engenheiro de Segurança
(1) Ponto de Dor: Ataques XSS levam ao vazamento de dados de usuários
Três meses após a plataforma SaaS TaskFlow da equipe de Bob ser lançada no Brasil, eles receberam um relatório de segurança:
"O atacante injetou a tag
<script>no campo 'Nome do Projeto' e conseguiu roubar cookies de sessão de mais de 200 usuários. A investigação revelou que o campo 'Nome do Projeto' na página do Dashboard não foi adequadamente escapado durante a saída."
A auditoria de segurança identificou uma série de problemas:
| Vulnerabilidade | Impacto | Severidade |
|---|---|---|
| XSS (Refletido) | Sequestro de sessão, roubo de dados | 🔴 Grave |
| Token CSRF Ausente | Cross-Site Request Forgery | 🟠 Alto Risco |
| Cabeçalhos de Segurança HTTP Ausentes | Clickjacking, MIME sniffing | 🟡 Médio |
| Uso indevido de NEXT_PUBLIC_ | Chave de API exposta no bundle JS | 🔴 Grave |
| Vulnerabilidade de Dependência | lodash — CVE conhecido, não corrigido | 🟠 Alto |
Ao mesmo tempo, a dívida técnica está se acumulando — o projeto é baseado no Next.js 14, mas o Next.js 16 foi lançado há seis meses, e a equipe está preocupada com os riscos de segurança e perdas de desempenho associadas à execução de uma versão desatualizada.
(2) Solução para Fortalecimento de Segurança + Migração de Versão
Bob aborda a segurança em duas etapas:
# Etapa 1: Corrigir Vulnerabilidades de Segurança Imediatamente
npm audit fix
# Adicionar um cabeçalho de segurança ao next.config.js
# Adicionar Token CSRF a Todos os Formulários
# Etapa 2: Migração de Versão (14→15→16)
npm install next@16 react@19 react-dom@19
(3) Resultados
| Dimensão | Antes do Reforço | Depois do Reforço |
|---|---|---|
| Pontuação de Segurança (Mozilla Observatory) | D (45/100) | A+ (100/100) |
| CVEs Conhecidos | 12 | 0 |
| Versão | Next.js 14.2 | Next.js 16.2 LTS |
| Tempo de Build | 45s | 12s (Turbopack) |
| Desempenho (Lighthouse) | 72 | 92 |
3. Configurando Cabeçalhos de Segurança HTTP
A função headers no next.config.js pode injetar cabeçalhos de resposta HTTP, sobrescrevendo políticas de segurança como CSP, HSTS e X-Frame-Options.
graph TB
A[Requisição do Usuário] --> B[Aplicações Next.js]
B --> C[Tratamento da Função headers]
C --> D[Injetar cabeçalhos de segurança]
D --> E[Política de Segurança do Navegador Ativada]
E --> F[CSP Bloqueia XSS]
E --> G[HSTS Força HTTPS]
E --> H[X-Frame-Options Previne Clickjacking]
style C fill:#cce5ff
style E fill:#d4edda
| Cabeçalho de Segurança | Finalidade | Valor de Exemplo |
|---|---|---|
Content-Security-Policy |
Prevenir XSS e Injeção de Dados | default-src 'self' |
Strict-Transport-Security |
Forçar HTTPS | max-age=31536000; includeSubDomains |
X-Frame-Options |
Prevenção de Clickjacking | SAMEORIGIN |
X-Content-Type-Options |
Prevenir MIME sniffing | nosniff |
Referrer-Policy |
Controlar Informações de Referrer | strict-origin-when-cross-origin |
Permissions-Policy |
Controlar Permissões da API do Navegador | camera=(), microphone=() |
(1) Configuração de Cabeçalhos de Segurança no next.config.js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
async headers() {
return [
{
// Aplicar a todas as rotas
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline' https://www.googletagmanager.com https://app.posthog.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' blob: data: https://*.taskflow.io https://avatars.githubusercontent.com",
"font-src 'self' data:",
"connect-src 'self' https://api.taskflow.io https://app.posthog.com https://o450000000.ingest.sentry.io",
"frame-ancestors 'none'",
"form-action 'self'",
"base-uri 'self'",
"object-src 'none'"
].join('; ')
},
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
},
{
key: 'Permissions-Policy',
value: [
'camera=()',
'microphone=()',
'geolocation=()',
'interest-cohort=()'
].join(', ')
},
{
key: 'X-XSS-Protection',
value: '1; mode=block'
}
]
},
{
// Restrições de Segurança Adicionais em Rotas de API
source: '/api/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Cache-Control',
value: 'no-store, no-cache, must-revalidate'
}
]
}
]
}
}
module.exports = nextConfig
(2) Proteção com Token CSRF
// src/components/Form.tsx
'use client'
import { useFormState } from 'react-dom'
import { createTask } from '@/actions/task'
export function TaskForm() {
const [state, formAction] = useFormState(createTask, { error: null })
return (
<form action={formAction}>
{/* Campos Ocultos do Token CSRF */}
<input type="hidden" name="csrf_token" value={getCsrfToken()} />
<input
type="text"
name="title"
placeholder="Título da tarefa"
className="border p-2 rounded"
/>
<button
type="submit"
className="bg-blue-600 text-white px-4 py-2 rounded"
>
Criar Tarefa
</button>
{state.error && (
<p className="text-red-600 mt-2">{state.error}</p>
)}
</form>
)
}
▶ Exemplo: Validação de Cabeçalhos
# Verificar Cabeçalhos de Segurança
curl -I https://taskflow.io
# Saída Esperada
HTTP/2 200
content-security-policy: default-src 'self'; script-src 'self' 'unsafe-eval'...
strict-transport-security: max-age=31536000; includeSubDomains
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
Saída:
{"status":"ok","data":{}}
4. Prevenindo Vazamentos de Variáveis de Ambiente
Variáveis com o prefixo NEXT_PUBLIC_ serão compiladas no bundle JS do lado do cliente — qualquer segredo colocado sob este prefixo será exposto ao navegador.
graph TB
A[.env.local] --> B{Prefixo da Variável de Ambiente}
B -->|NEXT_PUBLIC_*| C[Inline no bundle JS]
B -->|Outro| D[Apenas server-side]
C --> E[Visível no DevTools do Navegador]
D --> F[Seguro]
style C fill:#f8d7da
style D fill:#d4edda
style E fill:#f8d7da
| Prefixo da Variável | Escopo de Uso | Riscos de Segurança | Cenários Aplicáveis |
|---|---|---|---|
NEXT_PUBLIC_ |
Client + Server | 🔴 Alto (exposto no JS) | Chave pública de API, nome do projeto |
| Sem prefixo | Apenas server | 🟢 Seguro | Senhas de banco de dados, chaves de API |
process.env.* |
Injetado durante o build | 🟡 Médio (dependendo do ambiente de build) | Configurado durante o build |
(1) Checklist de Segurança de Variáveis de Ambiente
# ✅ Seguro: Usando Variáveis Sem Prefixo
DATABASE_URL=postgresql://user:pass@host:5432/db
AUTH_SECRET=super-secret-key-here
STRIPE_SECRET_KEY=sk_live_xxx
SENTRY_DSN=https://xxx@sentry.io/123
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
# ✅ Correto: NEXT_PUBLIC_ Para chaves públicas
NEXT_PUBLIC_POSTHOG_KEY=phc_publicKey
NEXT_PUBLIC_SENTRY_DSN=https://public@sentry.io/123
NEXT_PUBLIC_APP_URL=https://taskflow.io
# ❌ Perigo: Não armazene chaves sob NEXT_PUBLIC_
NEXT_PUBLIC_STRIPE_SECRET_KEY=sk_live_xxx # Exposto no navegador!
NEXT_PUBLIC_DATABASE_URL=postgresql://... # Conexões de Banco de Dados Expostas!
(2) Configuração do .gitignore
# .gitignore — Segurança de Variáveis de Ambiente
.env
.env*.local
.env.development.local
.env.test.local
.env.production.local
# Não ignore o .env.example (Este é um arquivo de modelo)
!.env.example
(3) Script de Verificação de Variáveis de Ambiente
// src/lib/env-validation.ts
// Garante que variáveis de ambiente sensíveis estejam inacessíveis no lado do cliente
function validateEnv() {
if (typeof window !== 'undefined') {
const nextPublicVars = Object.keys(process.env)
.filter(key => key.startsWith('NEXT_PUBLIC_'))
// Verificar exposição acidental de variáveis privadas
const sensitiveSuffixes = ['SECRET', 'KEY', 'PASSWORD', 'TOKEN', 'PRIVATE']
for (const key of nextPublicVars) {
const suffix = key.replace('NEXT_PUBLIC_', '')
if (sensitiveSuffixes.some(s => suffix.toUpperCase().includes(s))) {
console.warn(
`[AVISO DE SEGURANÇA] ${key} começa com NEXT_PUBLIC_ ` +
`mas contém palavra-chave sensível: ${suffix}. ` +
`Isso será exposto no bundle do cliente!`
)
}
}
}
}
validateEnv()
▶ Exemplo: Verificando Variáveis Expostas no Bundle
# Verificar se o JS do cliente contém alguma chave após o build
npm run build
# Procurar palavras-chave sensíveis nos artefatos de build
grep -r "sk_live_" .next/static/ 2>/dev/null || echo "✅ Nenhum segredo do Stripe no bundle do cliente"
grep -r "DATABASE_URL" .next/static/ 2>/dev/null || echo "✅ Nenhum DATABASE_URL no bundle do cliente"
grep -r "AUTH_SECRET" .next/static/ 2>/dev/null || echo "✅ Nenhum AUTH_SECRET no bundle do cliente"
Saída:
▲ Next.js 16.0.0
Creating an optimized production build ...
✓ Compiled successfully
✓ Linting and checking validity of types
✓ Collecting page data
✓ Generating static pages (5/5)
✓ Finalizing page optimization
Route (app) Size First Load JS
┌ ○ / 5.1 kB 89 kB
├ ○ /about 2.3 kB 86 kB
└ λ /api/items 0 B 84 kB
○ (Static) prerendered as static content
λ (Dynamic) server-rendered on demand
✅ Nenhum segredo do Stripe no bundle do cliente
✅ Nenhum DATABASE_URL no bundle do cliente
✅ Nenhum AUTH_SECRET no bundle do cliente
5. Auditorias de Segurança de Dependências
(1) npm audit
# Auditoria Básica
npm audit
# Filtrar por nível de severidade
npm audit --audit-level=high
# Correção Automática (Pode quebrar compatibilidade)
npm audit fix
# Corrigir apenas versões patch
npm audit fix --target=patch
# Gerar Relatório JSON
npm audit --json > security-report.json
(2) Integração com Snyk
# Instalação do Snyk CLI
npm install -g snyk
# Autenticação
snyk auth
# Testar Itens
snyk test
# Monitoramento Contínuo
snyk monitor
# Varredura de Imagem Docker
snyk container test taskflow:latest --file=Dockerfile
# .github/workflows/security.yml
name: Auditoria de Segurança
on:
push:
branches: [main]
schedule:
- cron: '0 6 * * 1' # Toda segunda-feira às 6h da manhã
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Instalar dependências
run: npm ci
- name: Executar npm audit
run: npx audit-ci --high --report-type summary
continue-on-error: true
- name: Varredura de Segurança Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: Fazer upload do SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: snyk.sarif
| Ferramenta | Escopo de Detecção | Integração CI | Correções Recomendadas |
|---|---|---|---|
| npm audit | Dependências Node.js | ✅ Integrado | npm audit fix |
| Snyk | Dependências + Docker + IaC | ✅ Action | Correção Automática via PR |
| GitHub Dependabot | Versões de dependências | ✅ Integrado | Atualizações automáticas via PR |
| CodeQL | Vulnerabilidade de Código Fonte | ✅ Action | Correção a Nível de Código |
6. Guia de Migração v14 para v15
A maior mudança no Next.js 15 é o tratamento assíncrono de params, cookies e headers — esta é a mudança mais disruptiva.
graph TB
subgraph "API Síncrona v14 (Obsoleta)"
A[params.id]
B[cookies().get()]
C[headers().get()]
end
subgraph "API Assíncrona v15 (Obrigatória)"
D[await params]
E[await cookies()]
F[await headers()]
end
G[generateMetadata também precisa de await]
H[Alterar todas as rotas dinâmicas para async]
D --> G
E --> G
F --> H
style A fill:#f8d7da
style B fill:#f8d7da
style C fill:#f8d7da
style D fill:#d4edda
style E fill:#d4edda
style F fill:#d4edda
(1) Checklist de Migração
| Mudança | v14 (Antigo) | v15 (Novo) | Arquivos Afetados |
|---|---|---|---|
| params | params.id (Síncrono) |
await params (Assíncrono) |
page.tsx, layout.tsx |
| searchParams | searchParams.page (síncrono) |
await searchParams (assíncrono) |
page.tsx |
| cookies | cookies().get() (síncrono) |
await cookies() (assíncrono) |
layout.tsx, middleware.ts |
| headers | headers().get() (síncrono) |
await headers() (assíncrono) |
layout.tsx, page.tsx |
| generateMetadata | params síncrono |
params assíncrono |
page.tsx |
| React | React 18 | React 19 | Global |
| useFormState | react-dom |
Renomeado para useActionState |
Componente de formulário |
(2) Exemplo de Migração de Síncrono para Assíncrono
// ❌ v14 (antigo) — params síncrono
export default function ProjectPage({
params
}: {
params: { id: string }
}) {
return <div>Projeto {params.id}</div>
}
// ✅ v15 (novo) — params assíncrono
export default async function ProjectPage({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return <div>Projeto {id}</div>
}
// ❌ v14 — generateMetadata síncrono
export function generateMetadata({ params }: { params: { id: string } }) {
return { title: `Projeto ${params.id}` }
}
// ✅ v15 — generateMetadata assíncrono
export async function generateMetadata({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return { title: `Projeto ${id}` }
}
▶ Exemplo: Tratamento Assíncrono de Cookies e Headers
Saída:
Renderiza a UI do componente generateMetadata.
// ❌ v14 — Obtenção síncrona de cookies
import { cookies } from 'next/headers'
export function Layout({ children }: { children: React.ReactNode }) {
const token = cookies().get('session_token')
return <div>{children}</div>
}
// ✅ v15 — Obtenção assíncrona de cookies
import { cookies } from 'next/headers'
export default async function Layout({
children
}: {
children: React.ReactNode
}) {
const cookieStore = await cookies()
const token = cookieStore.get('session_token')
return <div>{children}</div>
}
Saída:
Renderiza a UI do componente Layout.
7. Guia de Migração v15 para v16
O Next.js 16 introduz três grandes mudanças: proxy.ts substituindo middleware, Turbopack como padrão e a migração do use cache.
graph LR
A[v15] --> B{Migração v16}
B --> C[proxy.ts Substitui middleware<br/>Regras de Roteamento Configuráveis]
B --> D[Turbopack Padrão<br/>Remove configuração webpack]
B --> E[Migração use cache<br/>fetch cache → cacheTag]
C --> F[Configuração Mais Declarativa]
D --> G[Build 10x Mais Rápido]
E --> H[Controle de Cache Mais Granular]
style A fill:#f8d7da
style C fill:#d4edda
style D fill:#d4edda
style E fill:#d4edda
| Mudança | v15 (Antigo) | v16 (Novo) | Ação de Migração |
|---|---|---|---|
| Interceptação de Rota | middleware.ts (Funcional) |
proxy.ts (Configuracional) |
Extrair lógica do middleware para proxy.ts |
| Ferramentas de Build | webpack (padrão) | Turbopack (padrão) | Remover configuração webpack personalizada |
| Modelo de Cache | Cache Automático do fetch |
Diretiva use cache |
Migrar cache de dados para cacheTag |
| React | React 19 RC | React 19 Stable | Atualizar versão |
| PPR | experimental.ppr |
Versão estável | Remover o prefixo experimental. |
(1) Configuração do proxy.ts (Nova API do v16)
// src/proxy.ts — v16 Substitui middleware.ts
// ============================================
// proxy.ts é uma configuração declarativa de regras de roteamento
// ============================================
import { defineProxy } from 'next/proxy'
export default defineProxy({
// Regras de Correspondência de Rota
matcher: [
// Caminhos que Exigem Autenticação
{ pathname: '/dashboard/:path*', requireAuth: true },
{ pathname: '/projects/:path*', requireAuth: true },
{ pathname: '/settings/:path*', requireAuth: true },
// Regras de Redirecionamento
{ pathname: '/old-blog/:slug*', redirect: '/blog/:slug*' },
// Pular recursos estáticos
{ pathname: '/_next/:path*', bypass: true },
{ pathname: '/favicon.ico', bypass: true }
],
// Inspeção de Autenticação
async auth(request) {
const session = await getSession(request)
if (!session) {
return new Response(null, {
status: 307,
headers: { Location: '/login' }
})
}
return null // Permitido
},
// Injeção de Cabeçalhos de Requisição
async headers(request) {
return {
'x-request-id': crypto.randomUUID(),
'x-user-locale': getPreferredLocale(request)
}
}
})
// Funções Utilitárias
async function getSession(request: Request) {
const cookie = request.headers.get('cookie') || ''
const token = cookie.split('session_token=')?.[1]?.split(';')?.[0]
if (!token) return null
return { userId: 'user_123', role: 'admin' }
}
function getPreferredLocale(request: Request) {
const acceptLang = request.headers.get('accept-language') || 'en'
return acceptLang.split(',')[0].split('-')[0]
}
(2) Migração do use cache
// ❌ v15 — Usando cache do fetch
async function getProjects() {
const res = await fetch('https://api.example.com/projects', {
next: { revalidate: 300 }
})
return res.json()
}
// ✅ v16 — Usando use cache + cacheTag
import { cacheTag, cacheLife } from 'next/cache'
async function getProjects() {
'use cache'
cacheTag('projects')
cacheLife('hours')
const res = await fetch('https://api.example.com/projects')
return res.json()
}
(3) Configuração Padrão do Turbopack
// next.config.js — Versão Simplificada v16
/** @type {import('next').NextConfig} */
const nextConfig = {
// v16: Turbopack é o padrão. Nenhuma configuração necessária
// Remova a seguinte configuração do v15:
// swcMinify: true, // Padrão do Turbopack
// compiler: { ... }, // Já integrado
// webpack: (config) => config, // Remover webpack personalizado
output: 'standalone',
// v16 PPR Estável
ppr: true,
// Manter personalizações necessárias
serverExternalPackages: ['@prisma/client'],
images: {
formats: ['image/avif', 'image/webp']
}
}
module.exports = nextConfig
▶ Exemplo: Comparação do sistema antes e depois de uma migração completa
Saída:
Configuração do Next.js aplicada. As alterações entram em vigor na reinicialização do servidor.
// === Código Original v14 ===
// page.tsx
export default function Page({ params }: { params: { id: string } }) {
return <div>{params.id}</div>
}
// layout.tsx
import { cookies } from 'next/headers'
export default function Layout({ children }: { children: React.ReactNode }) {
const theme = cookies().get('theme')
return <div className={theme?.value}>{children}</div>
}
// middleware.ts
import { NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
if (!request.cookies.has('token')) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
// === Após a migração v16 ===
// page.tsx
export default async function Page({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return <div>{id}</div>
}
// layout.tsx
import { cookies } from 'next/headers'
export default async function Layout({
children
}: {
children: React.ReactNode
}) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return <div className={theme?.value}>{children}</div>
}
// proxy.ts (Substitui middleware.ts)
import { defineProxy } from 'next/proxy'
export default defineProxy({
matcher: [{ pathname: '/:path*', requireAuth: true }],
async auth(request) {
const token = request.headers.get('cookie')?.includes('token')
if (!token) return Response.redirect('/login')
return null
}
})
Saída:
Renderiza a UI do componente ▶ Exemplo: Comparação do sistema antes e depois de uma migração completa conforme descrito na seção.
8. Exemplo Completo: Fluxo de Trabalho de Fortalecimento de Segurança + Migração de Versão
# ============================================
# security-migration.sh
# Funcionalidades: Script Completo de Fortalecimento de Segurança + Migração de Versão
# ============================================
#!/bin/bash
set -euo pipefail
echo "=== Segurança e Migração do TaskFlow ==="
# Fase 1: Fortalecimento de Segurança
echo ""
echo "--- Fase 1: Fortalecimento de Segurança ---"
# 1.1 Auditoria de Dependências em Tempo de Execução
echo "Executando npm audit..."
npm audit --audit-level=high
npm audit fix --target=minor || true
# 1.2 Validação de Cabeçalhos
echo "Gerando relatório de cabeçalhos de segurança..."
curl -sI http://localhost:3000 | grep -E "^(content-security-policy|strict-transport-security|x-frame-options)" \
|| echo "AVISO: Cabeçalhos de segurança ausentes!"
# 1.3 Inspeção de Vazamento NEXT_PUBLIC
echo "Verificando segredos vazados..."
grep -r "NEXT_PUBLIC_.*SECRET\|NEXT_PUBLIC_.*KEY\|NEXT_PUBLIC_.*PASSWORD" \
.env* 2>/dev/null && echo "AVISO: Segredos em NEXT_PUBLIC!" || echo "✅ Nenhum segredo vazado"
# 1.4 Verificação de Proteção XSS
echo "Verificando proteção XSS..."
# Testar Codificação de Entrada
echo "<script>alert('xss')</script>" | node -e "
const { escape } = require('querystring')
process.stdin.on('data', d => console.log('Codificado:', escape(d.toString())))
"
# Fase 2: Migração de Versão
echo ""
echo "--- Fase 2: Migração de Versão ---"
# 2.1 Verificar a versão atual
CURRENT_VERSION=$(node -e "console.log(require('./node_modules/next/package.json').version)")
echo "Versão atual do Next.js: v$CURRENT_VERSION"
# 2.2 Backup
echo "Fazendo backup do node_modules atual..."
mv node_modules node_modules_v${CURRENT_VERSION}.bak
mv package-lock.json package-lock_v${CURRENT_VERSION}.json.bak
# 2.3 Atualizar
echo "Atualizando para Next.js 16..."
npm install next@latest react@latest react-dom@latest @types/react@latest @types/react-dom@latest
# 2.4 Executar codemod
echo "Executando codemods..."
npx @next/codemod@latest built-in-next-font .
npx @next/codemod@latest metadata-to-viewport-export .
npx @next/codemod@latest async-request-params .
# 2.5 Converter middleware para proxy.ts
if [ -f src/middleware.ts ]; then
echo "Convertendo middleware.ts para proxy.ts..."
mv src/middleware.ts src/middleware_v15.bak.ts
fi
# 2.6 Build e Verificação
echo "Construindo com Next.js 16..."
npm run build
# 2.7 Executar teste completo
echo "Executando suíte de testes..."
npm test
npm run test:e2e
echo ""
echo "=== Migração Concluída ==="
echo "Next.js $(node -e "console.log(require('./node_modules/next/package.json').version)")"
echo "React $(node -e "console.log(require('./node_modules/react/package.json').version)")"
echo "Build: ✅"
echo "Testes: ✅"
echo "Segurança: ✅"
// src/app/security/report/page.tsx
// ============================================
// Página de Relatório de Segurança
// ============================================
import { headers } from 'next/headers'
import { execSync } from 'child_process'
async function checkSecurityHeaders() {
const h = await headers()
const requiredHeaders = [
'content-security-policy',
'strict-transport-security',
'x-frame-options',
'x-content-type-options',
'referrer-policy',
'permissions-policy'
]
const results = requiredHeaders.map(name => ({
name,
present: h.has(name),
value: h.get(name) || ''
}))
const score = results.filter(r => r.present).length / results.length * 100
return { results, score }
}
async function checkDependencies() {
try {
const auditOutput = execSync('npm audit --json --audit-level=high', {
encoding: 'utf-8',
timeout: 30000
}).toString()
const audit = JSON.parse(auditOutput)
return {
vulnerabilities: audit.metadata?.vulnerabilities || { high: 0, critical: 0 },
total: audit.metadata?.totalDependencies || 0
}
} catch {
return { vulnerabilities: { high: 0, critical: 0 }, total: 0 }
}
}
const gradeMap: Record<number, string> = {
100: 'A+', 90: 'A', 80: 'B', 70: 'C', 60: 'D', 0: 'F'
}
function getGrade(score: number): string {
for (const [threshold, grade] of Object.entries(gradeMap)) {
if (score >= Number(threshold)) return grade
}
return 'F'
}
export default async function SecurityReportPage() {
const [securityCheck, deps] = await Promise.all([
checkSecurityHeaders(),
checkDependencies()
])
return (
<div className="p-6 space-y-6">
<h1 className="text-2xl font-bold">Relatório de Segurança</h1>
{/* Classificação de Segurança HTTP */}
<div className="bg-white rounded-lg shadow p-4">
<h2 className="text-lg font-semibold mb-4">
Cabeçalhos de Segurança HTTP
<span className="ml-2 px-2 py-1 text-sm rounded bg-blue-100">
Nota: {getGrade(securityCheck.score)}
</span>
</h2>
<table className="w-full text-sm">
<thead>
<tr className="text-left text-gray-500">
<th className="pb-2">Cabeçalho</th>
<th className="pb-2">Status</th>
<th className="pb-2">Valor</th>
</tr>
</thead>
<tbody>
{securityCheck.results.map(r => (
<tr key={r.name} className="border-t">
<td className="py-2 font-mono">{r.name}</td>
<td className="py-2">
{r.present ? '✅' : '❌'}
</td>
<td className="py-2 text-xs text-gray-500 truncate max-w-md">
{r.value || '—'}
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Segurança de Dependências */}
<div className="bg-white rounded-lg shadow p-4">
<h2 className="text-lg font-semibold mb-4">Auditoria de Dependências</h2>
<p>Total de pacotes: {deps.total}</p>
<div className="flex gap-4 mt-2">
<span className="text-red-600">
Crítico: {deps.vulnerabilities.critical}
</span>
<span className="text-orange-600">
Alto: {deps.vulnerabilities.high}
</span>
</div>
</div>
{/* Verificação de Variáveis de Ambiente */}
<div className="bg-white rounded-lg shadow p-4">
<h2 className="text-lg font-semibold mb-4">Variáveis de Ambiente</h2>
<p className="text-green-600">✅ Nenhum segredo NEXT_PUBLIC_ detectado</p>
</div>
</div>
)
}
❓ Perguntas Frequentes
P: O que devo fazer se a Content-Security-Policy (CSP) for muito restritiva e impedir o carregamento de certos scripts de terceiros? R: Use o modo CSP Report-Only para coletar relatórios de violação primeiro e mude para o modo de aplicação somente após confirmar que não há problemas. No
next.config.js, use o cabeçalhoContent-Security-Policy-Report-Onlyem conjunto comreport-urioureport-topara enviar relatórios ao Sentry ou a um endpoint personalizado.
P: Durante a migração v14→v15, o que é mais afetado pelas mudanças assíncronas em
params? R: Os componentes mais afetados sãogenerateMetadata,layout.tsxepage.tsx. Se o seu projeto contém um grande número de páginas que usamparams.id, você precisará verificá-las uma a uma durante a migração. Recomendamos executar primeiro a migração automatizadanpx @next/codemod@latest async-request-paramse depois verificar manualmente quaisquer casos de borda não cobertos.
P: O
proxy.tse omiddleware.tsoriginal são funcionalmente equivalentes? R: Oproxy.tsusa configuração declarativa (focando em "quais regras"), enquanto omiddleware.tsusa funções imperativas (focando em "como fazer"). Oproxy.tscobre 90% dos cenários de middleware (autenticação, redirecionamento, injeção de cabeçalhos), mas para lógica complexa de reescrita de requisições (como reescrita de rotas i18n), você ainda precisará usarmiddleware.ts(que ainda é suportado no v16).
P: O que devo fazer com minha configuração do Webpack após mudar para os padrões do Turbopack? R: O Turbopack suporta a maioria dos loaders e plugins do Webpack. Se você estiver usando uma configuração Webpack personalizada, recomendamos: (1) Primeiro, remover a configuração do Webpack e construir usando os padrões do Turbopack; (2) Se ocorrerem erros, verificar se há alternativas compatíveis com o Turbopack; (3) Nos raros casos em que as configurações são incompatíveis, você pode sobrescrevê-las no
next.config.jsusandoexperimental.turbo.rules. A equipe da Vercel planeja implementar compatibilidade total com configurações do Webpack no v16.3.
P: Qual é a diferença entre o cache
use cachee o cachefetchanterior? R: O cachefetché automático e implícito (padrãoforce-cache) e não pode ser controlado de forma refinada. Ouse cacheé uma diretiva de cache explícita em nível de componente que suportacacheTag(expiração por etiqueta) ecacheLife(políticas de ciclo de vida). Caminho de migração: (1) O cache fetch simples pode ser mantido; (2) Cenários que exigem invalidação sob demanda devem migrar parause cache+cacheTag; (3) Código novo deve usaruse cachediretamente.
P: O que deve ser feito como resposta de emergência após um vazamento de variável de ambiente? R: (1) Imediatamente rotacione todos os segredos no GitHub Secrets ou na plataforma de nuvem; (2) Verifique o histórico do Git em busca de vazamentos (
git log -p -S 'sk_live_'); (3) Se o segredo já foi exposto sobNEXT_PUBLIC_, assuma que todos os visitantes anteriores podem ter acessado e rotacione-o; (4) Adicionegithub-secret-scanninge.env*ao.gitignorepara prevenir outro vazamento.
📖 Resumo
- Cabeçalhos de segurança HTTP (CSP, HSTS, X-Frame-Options, etc.) são configurados na função
headersdonext.config.jse servem como a primeira linha de defesa contra XSS e clickjacking. - Variáveis com prefixo
NEXT_PUBLIC_são expostas ao JavaScript do lado do cliente; nunca as use para armazenar chaves..env*.localdeve estar incluído no.gitignore npm audite Snyk realizam varreduras regulares em busca de vulnerabilidades de dependências e integram verificações de auditoria de segurança no CI- Destaques da Migração v14→v15:
params/cookies/headersforam todos convertidos para assíncronos;useFormStatefoi renomeado parauseActionState - Principais mudanças na migração v15→v16: configuração declarativa
proxy.tssubstitui middleware funcionalmiddleware.ts; Turbopack se torna a ferramenta de build padrão; diretivasuse cachesubstituem o cache implícito dofetch - O fortalecimento de segurança e a migração de versão devem ser realizados em fases: primeiro, corrija vulnerabilidades de alto risco; depois, atualize a versão; cada etapa deve passar por testes e validação completos.
📝 Exercícios
-
Exercício Básico (⭐): Configure os seis cabeçalhos de segurança (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) no
next.config.jse usecurl -Iou o Mozilla Observatory para verificar se a configuração está ativa. -
Exercício Avançado (⭐⭐): Migre um projeto usando Next.js 14 para v16: (1) Execute
npx @next/codemod async-request-paramspara realizar uma migração automática; (2) Corrija manualmente quaisquer chamadas síncronas decookies()eheaders()não cobertas; (3) Convertamiddleware.tsparaproxy.ts(proteção de rota com autenticação); (4) Remova a configuraçãowebpacke verifique se o build com Turbopack é bem-sucedido. -
Desafio (⭐⭐⭐): Implemente um pipeline completo de CI/CD seguro: (1) Adicione controle de acesso
npm auditao GitHub Actions (bloqueie merges com nível de severidade "high" ou superior); (2) Integre varreduras Snyk e comente automaticamente os relatórios de vulnerabilidade nos pull requests; (3) Execute um script de validação de cabeçalhos de segurança após o build e procure vazamentosNEXT_PUBLIC_nos artefatos de build; (4) Execute automaticamente uma auditoria de segurança completa semanalmente e gere um relatório HTML.