Este documento mostra exemplos práticos dos principais riscos de segurança em aplicações Web (frontend), incluindo código vulnerável, ataques e formas de prevenção.
<!-- Comentários exibidos sem sanitização -->
<input id="comentario" placeholder="Digite um comentário" />
<div id="feed"></div>
<script>
function postar() {
const comentario = document.getElementById("comentario").value;
document.getElementById("feed").innerHTML += `<p>${comentario}</p>`;
}
</script>Um atacante insere:
<script>alert('Hacked!')</script>Quando renderizado:
<p><script>alert('Hacked!')</script></p>- Usar escape de HTML:
function sanitize(input) {
return input.replace(/</g, "<").replace(/>/g, ">");
}- Usar libs seguras (DOMPurify, React JSX → já faz escape automático).
Formulário de alteração de senha:
<form action="/alterar-senha" method="POST">
<input type="hidden" name="novaSenha" value="123456" />
<button type="submit">Alterar Senha</button>
</form>O atacante cria um site com:
<body onload="document.forms[0].submit()">
<form action="https://meusite.com/alterar-senha" method="POST">
<input type="hidden" name="novaSenha" value="hacked123" />
</form>
</body>meusite.com, o navegador envia os cookies automaticamente, alterando a senha sem consentimento.
- Tokens CSRF (ex.:
csrf_tokenescondido no form e validado no backend). SameSitenos cookies:
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure// Arquivo público: config.js
const API_KEY = "ABCD-1234-SECRET-KEY";
fetch(`https://api.pagamentos.com/?key=${API_KEY}&user=1`);- Nunca expor chaves no frontend.
- Usar backend intermediário que guarda a chave com segurança:
fetch("/api/pagamentos?user=1"); // backend adiciona API_KEYUm site legítimo pode ser embutido em um iframe:
<iframe src="https://banco.com" width="100%" height="600"></iframe>O atacante sobrepõe botões invisíveis (ex.: "Transferir dinheiro").
Adicionar cabeçalho HTTP no servidor:
X-Frame-Options: DENYou
Content-Security-Policy: frame-ancestors 'none';<button id="comprar" onclick="comprarProduto(100)">Comprar por R$100</button>
<script>
function comprarProduto(valor) {
alert("Produto comprado por R$" + valor);
}
</script>No console do navegador:
document.getElementById("comprar").setAttribute("onclick", "comprarProduto(1)");- Nunca confiar apenas no frontend.
- Validar regras no backend (ex.: preço real do produto).
- Usar event listeners em vez de
onclickinline.
- XSS → sanitizar entradas e usar frameworks que escapam HTML.
- CSRF → tokens anti-CSRF e
SameSitecookies. - Exposição de Dados → nunca expor chaves/tokens no frontend.
- Clickjacking →
X-Frame-Options/CSP frame-ancestors. - Manipulação de DOM → sempre validar no backend.
Este documento mostra exemplos práticos dos principais riscos de segurança em APIs e servidores backend, incluindo código vulnerável, ataques e formas de prevenção.
app.get("/user", (req, res) => {
const id = req.query.id;
db.query(`SELECT * FROM usuarios WHERE id = ${id}`, (err, result) => {
res.json(result);
});
});GET /user?id=1 OR 1=1
- Queries parametrizadas:
db.query("SELECT * FROM usuarios WHERE id = $1", [req.query.id]);- Usar ORMs seguros (Sequelize, TypeORM).
const token = jwt.sign({ id: user.id }, "segredo_super_fraco");expiresIn nunca expira → invasor pode usar indefinidamente.
- Usar tokens com expiração curta:
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "15m" });- Renovação via refresh token seguro.
- Revogar tokens comprometidos (blacklist/Redis).
// Rota de debug deixada exposta
app.get("/debug", (req, res) => {
res.json(process.env); // mostra variáveis sensíveis
});- Nunca expor endpoints de debug em produção.
- Usar feature flags ou habilitar só em dev:
if (process.env.NODE_ENV === "development") {
app.get("/debug", ...);
}// API retorna dados de qualquer usuário pelo ID
app.get("/user/:id", (req, res) => {
db.query("SELECT * FROM usuarios WHERE id = $1", [req.params.id], (err, result) => {
res.json(result.rows[0]);
});
});GET /user/2
- Validar ownerId:
app.get("/user/:id", (req, res) => {
if (req.user.id != req.params.id) return res.status(403).send("Acesso negado");
});app.post("/data", (req, res) => {
const objeto = eval("(" + req.body.payload + ")"); // péssima prática!
res.json(objeto);
});{ "payload": "process.exit()" }➡️ O servidor desliga.
- Usar JSON.parse ou libs seguras de deserialização.
- Validar payloads com Joi, Yup, Zod.
// Retorna tudo do usuário
app.get("/profile", (req, res) => {
res.json(user); // inclui senha, tokens, etc.
});- Retornar apenas os dados necessários:
res.json({ id: user.id, nome: user.nome, email: user.email });- Usar DTOs/serializers para controlar o que vai na resposta.
- Banco de dados acessível em
root/root. - Painel admin sem senha.
- Alterar credenciais padrão.
- Desabilitar usuários de superadmin em produção.
- Usar cofre de segredos (Vault, AWS Secrets Manager, .env).
- Injeções → queries parametrizadas, ORM, validação de input.
- Autenticação → tokens com expiração, MFA, refresh tokens.
- Endpoints Sensíveis → nunca expor debug/admin sem auth.
- Access Control → validar ownerId, RBAC/ABAC.
- Deserialização → nunca usar
eval, sempre validar payload. - Exposição de Dados → retornar só o necessário.
- Configurações → evitar credenciais default, usar cofre de segredos.