NextJS

Reading time: 25 minutes

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)

Support HackTricks

Arquitetura Geral de uma Aplicação Next.js

Estrutura de Arquivos Típica

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

lua
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 ativos estáticos, como imagens, fontes e outros arquivos. Os arquivos aqui são acessíveis no caminho raiz (/).
  • app/: Diretório central para as páginas, layouts, componentes e rotas da API da sua aplicação. Abrange o paradigma App Router, permitindo recursos avançados de roteamento e segregação de componentes servidor-cliente.
  • app/layout.tsx: Define o layout raiz para 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 o 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 page.tsx dentro dessas pastas corresponde ao componente da rota.
  • app/api/: Contém rotas da API, permitindo que você crie funções serverless que manipulam 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 de componente.
  • app/utils/: Inclui funções utilitárias, módulos auxiliares e outras lógicas não relacionadas à UI que podem ser compartilhadas pela aplicação.
  • .env.local: Armazena variáveis de ambiente específicas para o ambiente de desenvolvimento local. Essas variáveis não são comprometidas 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 definições do TypeScript para o projeto, habilitando verificação de tipos e outros recursos do TypeScript.
  • package.json: Gerencia as 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: Bloqueia as dependências do projeto em versões específicas, garantindo instalações consistentes em diferentes ambientes.

Lado do Cliente no 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.

Manipulando o Caminho Raiz /

Estrutura de Arquivos:

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

Arquivos Chave:

  • app/page.tsx: Manipula solicitações para o caminho raiz /.
  • app/layout.tsx: Define o layout para a aplicação, envolvendo todas as páginas.

Implementação:

tsx
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: 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 é envolto pelo layout.tsx, que pode incluir cabeçalhos, rodapés e outros elementos comuns.
Tratando Outros Caminhos Estáticos

Exemplo: Rota /about

Estrutura de Arquivos:

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

Implementação:

tsx
// 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 sobre.
Rotas Dinâmicas

Rotas dinâmicas permitem o manuseio de 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 Arquivo:

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

Implementação:

tsx
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:

  • Segmento Dinâmico: [id] denota um segmento dinâmico na rota, capturando o parâmetro id da URL.
  • Acessando Parâmetros: O objeto params contém os parâmetros dinâmicos, acessíveis dentro do componente.
  • Correspondência de Rota: Qualquer caminho que corresponda a /posts/*, como /posts/1, /posts/abc, etc., será tratado por este componente.
Rotas Aninhadas

Next.js suporta roteamento aninhado, permitindo estruturas de rota hierárquicas que refletem o layout do diretório.

Exemplo: Rota /dashboard/settings/profile

Estrutura de Arquivos:

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

Implementação:

tsx
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.
  • Reflexão de Hierarquia: A estrutura de diretórios reflete o caminho da URL, melhorando a manutenibilidade e clareza.
Rotas Catch-All

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

Exemplo: Rota /*

Estrutura de Arquivos:

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

Implementação:

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

  • Segmento Catch-All: [...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 Rota: Caminhos como /anything/here, /foo/bar/baz, etc., são tratados por este componente.

Potenciais Vulnerabilidades do Lado do Cliente

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

Cross-Site Scripting (XSS)

Os ataques XSS ocorrem quando scripts maliciosos são injetados em sites confiáveis. Os atacantes podem executar scripts nos navegadores dos usuários, roubando dados ou realizando ações em nome do usuário.

Exemplo de Código Vulnerável:

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

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

Injeção de Template do Lado do Cliente

Ocorre quando as entradas do usuário são manipuladas de forma inadequada em templates, permitindo que atacantes injetem e executem templates ou expressões.

Exemplo de Código Vulnerável:

jsx
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.

Travessia de Caminho do Cliente

É uma vulnerabilidade que permite que atacantes manipulem caminhos do lado do cliente para realizar ações não intencionais, como Cross-Site Request Forgery (CSRF). Ao contrário da travessia de caminho do lado do servidor, que visa o sistema de arquivos do servidor, a CSPT foca em explorar mecanismos do lado do cliente para redirecionar solicitações de API legítimas para pontos finais maliciosos.

Exemplo de Código Vulnerável:

Uma aplicação Next.js permite que os usuários façam upload e download de arquivos. O recurso de download é implementado no lado do cliente, onde os usuários podem especificar o caminho do arquivo a ser baixado.

jsx
// 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 exemplo, admin/config.json) manipulando o filePath.
  2. Explorando CSPT:
  • Entrada Maliciosa: O atacante cria uma URL com um filePath manipulado, como ../deleteFile/config.json.
  • Chamada de API Resultante: O código do lado do cliente faz uma solicitação para /api/files/../deleteFile/config.json.
  • Tratamento pelo Servidor: Se o servidor não validar o filePath, ele processa a solicitação, potencialmente deletando ou expondo arquivos sensíveis.
  1. Executando CSRF:
  • Link Criado: O atacante envia à vítima um link ou incorpora um script malicioso que aciona a solicitação de download com o filePath manipulado.
  • Resultado: A vítima executa a ação sem saber, levando ao 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 de filePath, possibilitando a travessia de caminho.
  • Confiando 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 o estado (por exemplo, deletar, modificar arquivos), pode ser explorado via CSPT.

Lado do Servidor em Next.js

Renderização do Lado do Servidor (SSR)

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

Casos de Uso:

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

Implementação:

jsx
// 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 no momento da construção, resultando em tempos de carregamento mais rápidos e carga reduzida no servidor.

Casos de Uso:

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

Implementação:

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

  • Manipulação 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 manuseio de APIs se tornaram mais flexíveis e poderosos. Essa abordagem moderna se alinha de perto com o sistema de roteamento baseado em arquivos, mas introduz capacidades aprimoradas, incluindo suporte para componentes de servidor e cliente.

Manipulador de Rota Básico

Estrutura de Arquivos:

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

Implementação:

javascript
// 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 da API estão localizadas no diretório app/api/.
  • Nomeação de Arquivos: Cada endpoint da API reside em sua própria pasta contendo um arquivo route.js ou route.ts.
  • Funções Exportadas: Em vez de uma única exportação padrão, funções específicas de métodos HTTP (por exemplo, GET, POST) são exportadas.
  • Manipulação de Respostas: Use o construtor Response para retornar respostas, permitindo mais controle sobre cabeçalhos e códigos de status.

Como lidar com outros caminhos e métodos:

Manipulação de Métodos HTTP Específicos

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

Exemplo:

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

javascript
// 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 abrangente, capturando todos os caminhos aninhados.
  • Uso: Útil para APIs que precisam lidar com profundidades de rota variadas ou segmentos dinâmicos.

Exemplo de Rotas Aninhadas:

javascript
// 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 de recursos.
  • Acesso a Parâmetros: Acesso fácil a múltiplos parâmetros de rota via o objeto params.
Manipulando 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 que o Next.js 13 introduzisse o diretório app e melhorasse as capacidades de roteamento, as rotas de API eram definidas principalmente dentro do diretório pages. Essa abordagem ainda é amplamente utilizada e suportada no Next.js 12 e versões anteriores.

Rota de API Básica

Estrutura de Arquivos:

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

Implementação:

javascript
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 da API residem no diretório pages/api/.
  • Exportação: Use export default para definir a função manipuladora.
  • Assinatura da Função: O manipulador 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:

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

Implementação:

javascript
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) denotam segmentos de rota dinâmicos.
  • Acessando Parâmetros: Use req.query.id para acessar o parâmetro dinâmico.
  • Tratando Métodos: Utilize lógica condicional para lidar com diferentes métodos HTTP (GET, PUT, DELETE, etc.).

Tratando Diferentes Métodos HTTP

Enquanto o exemplo básico de rota da API lida com todos os métodos HTTP dentro de uma única função, você pode estruturar seu código para lidar com cada método explicitamente para melhor clareza e manutenibilidade.

Exemplo:

javascript
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 Preocupações: Separe claramente a lógica para diferentes métodos HTTP.
  • Consistência de Resposta: Garanta estruturas de resposta consistentes para facilitar o manuseio do lado do cliente.
  • Tratamento de Erros: Lide graciosamente com métodos não suportados e erros inesperados.

Configuração de CORS

Controle quais origens podem acessar suas rotas de API, mitigando vulnerabilidades de Compartilhamento de Recursos de Origem Cruzada (CORS).

Exemplo de Configuração Ruim:

javascript
// 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 da API dentro do arquivo middleware.ts:

javascript
// 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 habilitar atacantes a realizar ações indesejadas.

Como os atacantes exploram isso:

Os atacantes podem criar sites maliciosos que fazem solicitações à sua API, potencialmente abusando de funcionalidades como recuperação de dados, manipulação de dados ou acionamento de 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 usado pelo servidor também no código exposto e usado pelo 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:

js
import "server-only"

Arquivos Chave e Seus Papéis

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 solicitação seja processada, permitindo tarefas como autenticação, redirecionamentos ou modificação de respostas.

Fluxo de Execução:

  1. Solicitação de Entrada: O middleware intercepta a solicitação.
  2. Processamento: Realiza operações com base na solicitação (por exemplo, verificar autenticação).
  3. Modificação da Resposta: Pode alterar a resposta ou passar o controle para o próximo manipulador.

Casos de Uso Exemplares:

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

Configuração de Exemplo:

typescript
// 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*"],
}

next.config.js

Localização: Raiz do projeto.

Propósito: Configura o comportamento do Next.js, habilitando ou desabilitando recursos, personalizando configurações do webpack, definindo variáveis de ambiente e configurando vários recursos de segurança.

Configurações de Segurança Chave:

Headers de Segurança

Headers de segurança aumentam a segurança 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 sniffing de tipo MIME:

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

Exemplos:

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

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 Ruim:

javascript
// 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. Ataque pode hospedar imagens contendo cargas úteis maliciosas ou conteúdo que engana os usuários.
  • Outro problema pode ser permitir um domínio onde qualquer um pode fazer upload de uma imagem (como raw.githubusercontent.com)

Como os atacantes abusam disso:

Ao injetar imagens de fontes maliciosas, os 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 Configuração Ruim:

javascript
// 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_, o Next.js não expõe variáveis para o cliente. No entanto, se prefixado por engano (por exemplo, NEXT_PUBLIC_SECRET_API_KEY), torna-se acessível no lado do cliente.

Como os atacantes abusam disso:

Se variáveis sensíveis forem expostas ao cliente, os 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 de sua aplicação, garantindo que os usuários sejam direcionados adequadamente sem introduzir vulnerabilidades de redirecionamento aberto.

a. Vulnerabilidade de Redirecionamento Aberto

Exemplo de Configuração Ruim:

javascript
// 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 os usuários especifiquem qualquer URL, possibilitando ataques de redirecionamento aberto.
  • Confiando na Entrada do Usuário: Redirecionar para URLs fornecidas pelos usuários sem validação pode levar a phishing, distribuição de malware ou roubo de credenciais.

Como os atacantes abusam disso:

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

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

Os usuários que confiam no domínio original podem, sem saber, navegar para sites prejudiciais.

Configuração do Webpack

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

a. Expondo Módulos Sensíveis

Exemplo de Configuração Ruim:

javascript
// 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: Alias de diretórios sensíveis e permitindo acesso do lado do cliente pode vazar informações confidenciais.
  • Agrupando Segredos: Se arquivos sensíveis forem agrupados para o cliente, seu conteúdo se torna acessível através de mapas de origem ou inspecionando o código do lado do cliente.

Como os atacantes abusam disso:

Os 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 e 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:

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

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

export default MyApp

pages/_document.js

Propósito: Substitui o Documento 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:

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

Propósito: Embora o Next.js venha com um servidor embutido, você pode criar um servidor personalizado para casos de uso avançados, como roteamento personalizado 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:

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

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

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 de Forma Segura: 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 lado do servidor.

Exemplo:

javascript
// 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 no cliente.

Autenticação e Autorização

Abordagem:

  • Autenticação Baseada em Sessão: Use cookies para gerenciar sessões de usuários.
  • Autenticação Baseada em Token: Implemente JWTs para autenticação sem estado.
  • Provedores de Terceiros: Integre-se com provedores OAuth (por exemplo, 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:

javascript
// 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 Imagem: Use o componente next/image do Next.js para otimização automática de imagens.
  • Divisão de Código: Aproveite as importações dinâmicas para dividir o código e reduzir os tempos de carregamento iniciais.
  • Cache: Implemente estratégias de cache para respostas de API e ativos estáticos.
  • Carregamento Sob Demanda: Carregue componentes ou ativos apenas quando forem necessários.

Exemplo:

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

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

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)

Support HackTricks