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:
public/: Hospeda ativos estáticos, como imagens, fontes e outros arquivos. Os arquivos aqui são acessíveis na 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 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 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 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 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 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.
// app/about/page.tsxexportdefaultfunctionAboutPage() {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.
tsxCopy code// app/posts/[id]/page.tsximport { useRouter } from'next/navigation';interfacePostProps {params: { id:string };}exportdefaultfunctionPostPage({ 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 rotas hierárquicas que refletem o layout do diretório.
tsxCopy code// app/dashboard/settings/profile/page.tsxexportdefaultfunctionProfileSettingsPage() {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.
// app/[...slug]/page.tsxinterfaceCatchAllProps {params: { slug:string[] };}exportdefaultfunctionCatchAllPage({ params }:CatchAllProps) {const { slug } = params;constfullPath=`/${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:
// Dangerous: Injecting user input directly into HTMLfunctionComment({ userInput }) {return <divdangerouslySetInnerHTML={{ __html: userInput }} />;}
Por que é vulnerável: Usar dangerouslySetInnerHTML com entradas não confiáveis 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:
import React from'react';import ejs from'ejs';functionRenderTemplate({ template, data }) {consthtml=ejs.render(template, data);return <divdangerouslySetInnerHTML={{ __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.
Traversal 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 do traversal de caminho do lado do servidor, que visa o sistema de arquivos do servidor, o CSPT foca em explorar mecanismos do lado do cliente para redirecionar solicitações de API legítimas para endpoints 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.
Objetivo do Atacante: Realizar um ataque CSRF para deletar um arquivo crítico (por exemplo, admin/config.json) manipulando o filePath.
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 do Servidor: Se o servidor não validar o filePath, ele processa a solicitação, potencialmente deletando ou expondo arquivos sensíveis.
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 deleçã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 motores de busca podem rastrear a página totalmente renderizada.
Implementação:
// pages/index.jsexportasyncfunctiongetServerSideProps(context) {constres=awaitfetch('https://api.example.com/data');constdata=awaitres.json();return { props: { data } };}functionHomePage({ data }) {return <div>{data.title}</div>;}exportdefault 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 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.jsexportasyncfunctiongetStaticProps() {constres=awaitfetch('https://api.example.com/data');constdata=awaitres.json();return { props: { data }, revalidate:60 }; // Revalidate every 60 seconds}functionHomePage({ data }) {return <div>{data.title}</div>;}exportdefault 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 API 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.
// app/api/hello/route.jsexportasyncfunctionPOST(request) {returnnewResponse(JSON.stringify({ message:'Hello from App Router!' }), {status:200,headers: { 'Content-Type':'application/json' },});}// Client-side fetch to access the API endpointfetch('/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:
Manipulando 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:
// app/api/users/[id]/route.jsexportasyncfunctionGET(request, { params }) {const { id } = params;// Fetch user data based on 'id'returnnewResponse(JSON.stringify({ userId: id, name:'Jane Doe' }), {status:200,headers: { 'Content-Type':'application/json' },});}exportasyncfunctionPUT(request, { params }) {const { id } = params;// Update user data based on 'id'returnnewResponse(JSON.stringify({ message:`User ${id} updated.` }), {status:200,headers: { 'Content-Type':'application/json' },});}exportasyncfunctionDELETE(request, { params }) {const { id } = params;// Delete user based on 'id'returnnewResponse(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.
Sintaxe:[...] denota um segmento que captura todos os caminhos aninhados.
Uso: Útil para APIs que precisam lidar com profundidades de rota variadas ou segmentos dinâmicos.
Exemplo de Rotas Aninhadas:
// app/api/posts/[postId]/comments/[commentId]/route.jsexportasyncfunctionGET(request, { params }) {const { postId,commentId } = params;// Fetch specific comment for a postreturnnewResponse(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: Acesse facilmente 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.
javascriptCopy code// pages/api/users/[id].jsexportdefaultfunctionhandler(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 tratar diferentes métodos HTTP (GET, PUT, DELETE, etc.).
Tratando Diferentes Métodos HTTP
Enquanto o exemplo básico de rota de API trata todos os métodos HTTP dentro de uma única função, você pode estruturar seu código para tratar cada método explicitamente para melhor clareza e manutenibilidade.
Exemplo:
javascriptCopy code// pages/api/posts.jsexportdefaultasyncfunctionhandler(req, res) {const { method } = req;switch (method) {case'GET':// Handle GET requestres.status(200).json({ message:'Fetching posts.' });break;case'POST':// Handle POST requestres.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 Cross-Origin Resource Sharing (CORS).
Exemplo de Configuração Ruim:
// app/api/data/route.jsexportasyncfunctionGET(request) {returnnewResponse(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:
// app/middleware.tsimport { NextResponse } from'next/server';importtype { NextRequest } from'next/server';exportfunctionmiddleware(request:NextRequest) {constallowedOrigins= ['https://yourdomain.com','https://sub.yourdomain.com'];constorigin=request.headers.get('Origin');constresponse=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 requestsif (request.method ==='OPTIONS') {returnnewResponse(null, {status:204,headers:response.headers,});}return response;}exportconstconfig= {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.
Ampla Permissão de Métodos: Permitir todos os métodos pode permitir que atacantes realizem 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.
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:
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:
Solicitação de Entrada: O middleware intercepta a solicitação.
Processamento: Realiza operações com base na solicitação (por exemplo, verificar autenticação).
Modificação da Resposta: Pode alterar a resposta ou passar o controle para o próximo manipulador.
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.
Principais Configurações de Segurança:
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 detecção de tipo MIME:
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:
// next.config.jsmodule.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. Ataques podem 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:
// next.config.jsmodule.exports= {env: {SECRET_API_KEY:process.env.SECRET_API_KEY,// Exposed to the clientNEXT_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:
// next.config.jsmodule.exports= {asyncredirects() {return [{source:'/redirect',destination: (req) =>req.query.url,// Dynamically redirects based on query parameterpermanent: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:
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.
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.
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.
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:
// next.config.jsmodule.exports= {env: {API_KEY:process.env.API_KEY,// Accessible on both client and serverSECRET_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.