Segurança ofensiva na prática: XSS, SQL injection e arquitetura de confiança zero em next.js
Voltar para blog

Segurança ofensiva na prática: XSS, SQL injection e arquitetura de confiança zero em next.js

07/06/2026 · 5 min · Cibersegurança

Este artigo consolida falhas críticas que identifiquei em auditorias de segurança de aplicações SaaS. O risco nestes cenários não é meramente técnico: envolve prejuízos financeiros diretos, danos à reputação e sérias implicações jurídicas na LGPD. O objetivo aqui é documentar a engenharia de segurança de forma prática e aplicável em ambientes reais de produção.

Quando uma aplicação cresce, o erro mais comum dos desenvolvedores é assumir que frameworks modernos como o Next.js resolvem a segurança de forma mágica. Não resolvem. Segurança é uma arquitetura coordenada em camadas de validação, persistência, controle de sessão, criptografia de dados e execução em tempo de execução.


1) SQL injection e autenticação insegura#

Vulnerabilidades de injeção SQL acontecem quando entradas fornecidas pelo usuário são interpoladas diretamente dentro da string de consulta do banco de dados, permitindo que atacantes manipulem a lógica da query.

Anti-pattern crítico (vulnerável)#

// /pages/api/login.js - VULNERÁVEL
const { email, password } = req.body;
// Interpolação direta e checagem de senha em texto plano (inseguro)
const sql = `SELECT * FROM users WHERE email = '${email}' AND password = '${password}'`;
const result = await db.query(sql);

Com um payload básico como ' OR 1=1 --, o atacante anula a checagem da senha e realiza login como o primeiro usuário retornado pelo banco (geralmente o administrador).

Correção definitiva: prepared statements + password hashing (bcrypt)#

Para corrigir essa falha de forma profissional, separamos a instrução SQL dos parâmetros de dados e validamos a senha usando hash seguro (com bcrypt ou argon2):

import bcrypt from 'bcrypt';

// 1. Prepared Statement: busca apenas pelo e-mail
const sql = "SELECT id, email, password_hash FROM users WHERE email = ?";
const result = await db.query(sql, [email]);
const user = result.rows[0];

// 2. Comparação segura do hash da senha
if (user && await bcrypt.compare(password, user.password_hash)) {
  // 3. Prevenção de Session Fixation: regenera a sessão após login bem-sucedido
  req.session.regenerate((err) => {
    if (err) return next(err);
    req.session.userId = user.id;

    // 4. Log de auditoria estruturado (JSON format)
    console.log(JSON.stringify({
      event: 'LOGIN_SUCCESS',
      userId: user.id,
      ip: req.ip,
      timestamp: new Date().toISOString()
    }));

    res.json({ success: true });
  });
} else {
  // Log estruturado de falha
  console.log(JSON.stringify({
    event: 'LOGIN_FAILURE',
    email: email,
    ip: req.ip,
    timestamp: new Date().toISOString()
  }));
  res.status(401).json({ error: 'Credenciais inválidas' });
}

O perigo oculto em orms#

Mesmo utilizando ORMs consolidados como o Prisma, desenvolvedores frequentemente recorrem a métodos como $queryRawUnsafe para consultas complexas. Isso reabre a superfície de injeção. Em ambientes de produção:


2) Rate limiting em endpoints críticos#

Endpoints de autenticação expostos sem controle de taxa de requisições facilitam ataques de força bruta. Devemos limitar as tentativas de login por IP.

Implementação de rate limiting com express-rate-limit:#

const rateLimit = require('express-rate-limit');

export const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // Janela de 15 minutos
  max: 5, // Limite de 5 tentativas por IP por janela
  message: { error: 'Muitas tentativas de login. Tente novamente em 15 minutos.' },
  standardHeaders: true, // Retorna dados do limite nos cabeçalhos RateLimit-*
  legacyHeaders: false, // Desabilita cabeçalhos X-RateLimit-* legados
});

3) Proteção contra CSRF e validação de content-type#

Ataques de Cross-Site Request Forgery (CSRF) forçam o navegador do usuário autenticado a enviar requisições maliciosas para endpoints internos do SaaS.

Implementação de validação de CSRF token em API routes:#

import csurf from 'csurf';

const csrfProtection = csurf({ cookie: true });

export default async function handler(req, res) {
  // Validar CSRF Token em requisições de mutação
  if (['POST', 'PUT', 'DELETE'].includes(req.method)) {
    const csrfToken = req.body._csrf || req.headers['x-csrf-token'];
    if (!csrfToken || csrfToken !== req.session.csrfToken) {
      return res.status(403).json({ error: 'CSRF token inválido ou ausente.' });
    }
  }
  
  // Continuar processamento...
}

Validando o content-type header#

Outro ataque comum envolve o "Content-Type Sniffing", onde atacantes tentam enviar payloads maliciosos disfarçados de outros formatos. Validamos o cabeçalho explicitamente:

if (['POST', 'PUT'].includes(req.method)) {
  const contentType = req.headers['content-type'];
  if (!contentType || !contentType.includes('application/json')) {
    return res.status(415).json({ error: 'Content-Type inválido. Apenas application/json é permitido.' });
  }
}

4) XSS e sanitização na camada de execução#

Stored XSS (Cross-Site Scripting Persistido) em ambientes multi-tenant permite que dados maliciosos salvos por um usuário executem scripts no navegador de outros clientes ou de administradores.

Sanitização com dompurify#

Ao lidar com saídas de editores de texto rico (WYSIWYG), nunca injete o conteúdo diretamente sem sanitizar.

import DOMPurify from "dompurify";

// Sanitizar a entrada no frontend antes de renderizar
const safeContent = DOMPurify.sanitize(userInput);
return <div dangerouslySetInnerHTML={{ __html: safeContent }} />;

Regras operacionais para XSS:#

  1. Sanitização na Ingestão (Backend): Sempre limpe tags HTML proibidas e scripts antes de salvar no banco de dados;
  2. Sanitização na Renderização (Frontend): Utilize DOMPurify ao injetar strings HTML na DOM;
  3. Content Security Policy (CSP): Configure uma CSP rígida para restringir fontes de execução de scripts de terceiros.

5) Validação de inputs estrita com zod#

Toda entrada que entra na aplicação deve passar por validações de tipo, formato e tamanho máximo para evitar ataques de estouro de pilha (stack overflow) e Denial of Service (DoS).

Validação de esquema com limites de comprimento#

import { z } from 'zod';

export const userSchema = z.object({
  name: z.string().min(3).max(100).trim(), // Limita tamanho máximo do input
  email: z.string().email().max(255).toLowerCase(),
  role: z.enum(["USER", "CLIENT"]).default("USER"),
}).strict(); // strict() impede parâmetros extras indesejados (Mass Assignment)

No endpoint do Next.js:

const parsed = userSchema.safeParse(req.body);
if (!parsed.success) {
  return res.status(400).json(parsed.error.format());
}

6) HTTPS enforcement e cookies de sessão seguros#

A integridade do canal de transporte (HTTPS) é a linha base para evitar interceptações de tráfego (man-in-the-middle).

Redirecionamento para HTTPS em middleware do next.js:#

// middleware.js
export function middleware(request) {
  const forwardedProto = request.headers.get('x-forwarded-proto');
  const protocol = forwardedProto || request.nextUrl.protocol;

  if (protocol !== 'https:' && process.env.NODE_ENV === 'production') {
    const url = request.nextUrl.clone();
    url.protocol = 'https:';
    return Response.redirect(url);
  }
}

Configurando cookies de sessão seguros (nextauth)#

Armazenar sessões ou JWTs no localStorage deixa os tokens expostos a roubos via XSS. Utilize cookies com diretivas estritas:

const cookies = {
  sessionToken: {
    name: `__Secure-next-auth.session-token`, // Prefixo __Secure- exige HTTPS
    options: {
      httpOnly: true, // Bloqueia leitura via document.cookie no JavaScript
      sameSite: 'lax', // Mitigação de CSRF
      path: '/',
      secure: true, // Transmissão apenas via HTTPS
    },
  },
};

7) Tratamento seguro de erros e headers no next.config.js#

Expor logs de banco de dados ou detalhes internos do servidor em mensagens de erro do cliente facilita a descoberta de vulnerabilidades por atacantes.

Tratamento seguro de exceções#

try {
  const result = await db.query(sql, params);
  res.json(result);
} catch (error) {
  // 1. Log interno detalhado para o time de SRE/DevSecOps
  console.error('Database query failure:', error);

  // 2. Resposta genérica e segura para o usuário externo
  res.status(500).json({ error: 'Erro interno do servidor.' });
}

Cabeçalhos de segurança no next.config.js#

Adicione cabeçalhos HTTP de defesa diretamente na configuração do Next.js:

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'X-Frame-Options', value: 'DENY' },
          { key: 'X-XSS-Protection', value: '1; mode=block' },
          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
          { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
          {
            key: 'Content-Security-Policy',
            value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
          },
        ],
      },
    ];
  },
};

8) Ferramentas de auditoria e teste de segurança#

Automatize testes de segurança e validações de vulnerabilidades no seu pipeline de CI/CD:

# 1. Scanner de segurança automático OWASP ZAP (análise de baseline)
docker run -t ghcr.io/zaproxy/zaproxy zap-baseline.py -t https://seu-app.com

# 2. Verificar vulnerabilidades conhecidas em dependências do npm
npm audit
npm audit fix

# 3. Análise estática de código com foco em segurança (Snyk)
npx snyk test

# 4. ESLint com regras específicas para segurança em JavaScript/TypeScript
npm install eslint-plugin-security --save-dev

Tabela de referência: OWASP top 10 vs next.js#

OWASPVulnerabilidadePrevenção no Next.js
A01Broken Access ControlMiddleware global + RBAC estrito
A02Cryptographic FailuresCriptografia bcrypt/argon2 + HTTPS com HSTS
A03InjectionPrepared Statements + Validação Zod + $queryRaw seguro
A04Insecure DesignModelagem de ameaças (Threat Modeling) no ciclo ágil
A05Security MisconfigurationCabeçalhos HTTP no next.config.js + CSP rígida
A06Vulnerable ComponentsAuditoria sistemática via npm audit e Snyk
A07Auth FailuresMulti-factor auth + Rate Limiting + Regeneração de sessões
A08Data IntegrityValidação estruturada de inputs + Subresource Integrity
A09Logging FailuresLogs formatados em JSON + Centralização em SIEM
A10SSRFValidação e whitelist de requisições externas em SSR

Runbook: auditoria de segurança next.js#

Siga este roteiro estruturado para auditar a segurança de suas aplicações baseadas em Next.js:

1. Configuração e dependências#

2. Autenticação e controle de sessão#

3. Entrada e sanitização de dados#

4. Resposta e monitoramento#


Considerações práticas#

Garantir a integridade de uma aplicação SaaS baseada em Next.js exige atenção constante à fronteira de inputs, armazenamento de chaves de sessão e tratamento robusto de dados no banco. Aplicar práticas de menor privilégio e defesa em profundidade mitiga os riscos antes que eles se transformem em incidentes reais de segurança.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários