Por que a segurança é responsabilidade de todos
A segurança web não é uma preocupação exclusiva de especialistas que outra pessoa resolve. Cada formulário, cada comentário renderizado, cada cookie e cada endpoint de API é uma oportunidade para um erro que expõe seus usuários. O custo de um bug de segurança é excepcionalmente alto: perda de dados, quebra de confiança e exposição jurídica.
A boa notícia é que a maioria dos ataques segue um pequeno número de padrões, e a maioria das defesas é bem compreendida. Este guia aborda o essencial: cross-site scripting, cross-site request forgery, CORS, autenticação segura e os headers do navegador que aplicam suas regras.
A regra de ouro
Nunca confie no cliente. Qualquer coisa que venha do navegador pode ser forjada: valores de formulários, headers, campos ocultos, preços, IDs de usuário e o estado do JavaScript. A validação no client-side serve para a experiência do usuário; o servidor deve validar e autorizar tudo novamente.
Este princípio único fundamenta quase todas as defesas. Se você assumir que a entrada de dados é hostil e que as permissões devem ser verificadas no server-side, a maioria das vulnerabilidades desaparece antes mesmo de serem escritas.
Cross-site scripting (XSS)
XSS é a vulnerabilidade web grave mais comum. Ela ocorre quando dados controlados por um atacante são tratados como código. Existem três tipos principais: stored (salvos no servidor e servidos para outros usuários), reflected (refletidos de volta em uma resposta) e DOM-based (introduzidos por JavaScript no lado do cliente).
A principal defesa é a codificação de saída (output encoding): codifique os dados para o contexto onde eles aparecem. No DOM, use textContent em vez de innerHTML.
// safe.js
const el = document.createElement("p");
el.textContent = userComment; // escaped
// If you truly need HTML, sanitise it
import DOMPurify from "dompurify";
el.innerHTML = DOMPurify.sanitize(userHtml);
No servidor, utilize templates que façam o escape por padrão e nunca construa SQL ou HTML através de concatenação de strings. Para bancos de dados, use sempre queries parametrizadas ou um ORM para que a entrada nunca se torne SQL.
Content Security Policy
Uma Content Security Policy é um cabeçalho de resposta que informa ao navegador quais fontes uma página pode utilizar. Uma política rigorosa transforma muitos bugs de XSS em erros inofensivos no console.
Content-Security-Policy:
default-src 'self';
script-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Comece com Content-Security-Policy-Report-Only para coletar violações sem quebrar o site e, depois, aplique a política. Evite unsafe-inline e unsafe-eval sempre que possível, e utilize nonces ou hashes para os scripts inline que você não conseguir remover.
Cross-site request forgery (CSRF)
O CSRF engana o navegador para enviar uma requisição autenticada utilizando os cookies da vítima. Se o seu site utiliza sessões baseadas em cookies e um endpoint que altera o estado aceita uma requisição simples, a página de um atacante pode dispará-la.
Defesas em camadas:
- Cookies SameSite.
SameSite=LaxouStrictimpede que os cookies sejam enviados em requisições cross-site em navegadores modernos. - Tokens Anti-CSRF. Um token por sessão incluído em formulários e verificado no servidor.
- Verifique a origem. Rejeite requisições que alterem o estado cujo header
Originnão seja o do seu site. - Nunca realize mutações em GET. Use POST, PUT, PATCH ou DELETE para alterações.
CORS
CORS é uma regra do navegador sobre quais origens podem ler respostas da sua API. Por padrão, uma página não pode ler uma resposta de origem cruzada (cross-origin), a menos que o servidor a permita via Access-Control-Allow-Origin.
Dois pontos são comumente mal compreendidos:
- O CORS protege o navegador do usuário, não o seu servidor. Outros servidores podem chamar sua API livremente; apenas a autorização no lado do servidor pode impedi-los.
- Um
Access-Control-Allow-Origin: *permissivo combinado com credenciais não é permitido, e refletir origens arbitrárias é perigoso.
Configure o CORS explicitamente para as origens em que você confia e mantenha a autenticação e autorização no servidor.
Autenticação segura
A autenticação é onde os erros são mais dispendiosos.
- Faça o hash de senhas com bcrypt, scrypt ou Argon2; nunca utilize texto simples ou criptografia reversível.
- Use cookies HttpOnly, Secure e SameSite para sessões, para que o JavaScript não consiga ler o token.
- Prefira tokens de curta duração com refresh, e realize a rotação deles.
- Adicione autenticação de múltiplos fatores para contas sensíveis.
- Aplique rate-limit no login e bloqueie o acesso após falhas repetidas.
- Nunca coloque segredos no código do cliente — qualquer coisa enviada ao navegador é pública.
A comparação acima mostra a troca entre cookies versus localStorage. Tokens localStorage são convenientes, mas podem ser lidos por qualquer script, portanto, um único XSS rouba a sessão. Cookies HttpOnly eliminam esse risco.
Outros essenciais
- HTTPS em todo lugar, com HSTS para evitar ataques de downgrade.
- Valide e codifique a entrada no servidor para cada campo.
- Aplique o princípio do menor privilégio a usuários de banco de dados, API keys e roles de nuvem.
- Configure headers de segurança: CSP, HSTS,
X-Content-Type-Options: nosniff,Referrer-Policyeframe-ancestors. - Mantenha as dependências atualizadas e faça auditorias regularmente.
- Não vaze detalhes em mensagens de erro ou stack traces em produção.
- Registre e monitore eventos de autenticação e falhas de permissão.
Melhores práticas
- Trate toda entrada do cliente como não confiável e valide-a no servidor.
- Codifique a saída para o seu contexto; use
textContente sanitize qualquer HTML. - Use queries parametrizadas para todo acesso ao banco de dados.
- Armazene sessões em cookies HttpOnly, Secure e SameSite.
- Adicione uma Content Security Policy e comece com o modo report-only.
- Use SameSite junto com tokens anti-CSRF para requisições que alterem o estado.
- Mantenha segredos no servidor e realize a rotação deles.
Erros comuns
- Usar
innerHTMLcom dados de usuário. - Colocar tokens de sessão em
localStorage. - Confiar na validação do lado do cliente ou em campos ocultos de formulários.
- Construir SQL usando concatenação de strings.
- Assumir que CORS é um mecanismo de controle de acesso.
- Enviar chaves de API no código do front-end.
Próximos passos
Segurança é uma prática, não um checklist que você finaliza. Baseie-a no protocolo HTTP, aplique-a no servidor Node.js e gerencie segredos com segurança ao fazer o deploy com Docker. Leia o OWASP Top 10 e, em seguida, faça a auditoria de um formulário do seu próprio app de ponta a ponta — você quase sempre encontrará algo que valha a pena corrigir.