Pular para o conteúdo

Boas práticas e padrões · Segurança no código · 6 min de leitura

Validação de entrada e injeção (SQL, XSS, comandos)

Todo dado que vem de fora é suspeito. Valide no servidor, nunca monte SQL, HTML ou comandos concatenando texto do usuário — use parâmetros e escape por contexto.

Entrada → Validação1Validação → Consulta parametrizada2Consulta parametrizada → Banco3Banco → Saída escapada4Entrada — form, URL, APIEntradaform, URL, APIValidação — tipo, tamanho, formatoValidaçãotipo, tamanho, formatoConsulta parametrizada — WHERE email = ?Consulta parametrizadaWHERE email = ?BancoBancoSaída escapada — por contexto (HTML, URL)Saída escapadapor contexto (HTML, URL)

Arraste para o lado para ver o diagrama inteiro.

Validação reduz o lixo que entra; parâmetros e escape garantem que o dado nunca vire código, mesmo se a validação falhar.

Injeção

Injeção (A05 no OWASP Top 10:2025) acontece quando um dado é interpretado como código: "SELECT * FROM users WHERE email = '" + email + "'" com o e-mail ' OR '1'='1 devolve todos os usuários. O mesmo vale para HTML (XSS), comandos do sistema, LDAP e caminhos de arquivo (../../).

A defesa não é "limpar caracteres perigosos", é separar dado de código: consultas parametrizadas (ou ORM), APIs que recebem argumentos em lista em vez de uma string de shell, e escape automático do framework de templates.

Validação

  • No servidor, sempre. A validação no app ou no navegador é conforto, não segurança: qualquer um chama a API direto.
  • Lista do que é permitido (formato, tamanho, intervalo, enum) é melhor que lista do que é proibido.
  • Valide com um esquema (Zod, kotlinx.serialization + regras, Codable + checagens) na borda e trabalhe com tipos já válidos por dentro.

XSS

No navegador, nunca coloque texto do usuário em innerHTML; use textContent ou deixe o framework escapar. Se precisar de HTML do usuário, passe por um sanitizador (DOMPurify) e configure uma Content-Security-Policy.

No código: antes e depois

✗ AntesTypeScript
app.get('/usuarios', async (req, res) => {
  const email = req.query.email as string;
  const rows = await db.query(`SELECT * FROM usuarios WHERE email = '${email}'`); // injeção
  res.send(`<h1>Olá, ${rows[0]?.nome}</h1>`); // XSS se o nome tiver <script>
});
✓ DepoisTypeScript
import { z } from 'zod';

const Query = z.object({ email: z.string().email().max(254) });

app.get('/usuarios', async (req, res) => {
  const parsed = Query.safeParse(req.query);
  if (!parsed.success) return res.status(400).json({ erro: 'e-mail inválido' });

  // O driver envia o valor separado do SQL: ele nunca é interpretado como código
  const rows = await db.query('SELECT nome FROM usuarios WHERE email = $1', [parsed.data.email]);
  res.json({ nome: rows[0]?.nome ?? null }); // JSON: o front mostra com textContent
});

Validação na borda, parâmetro $1 no lugar da concatenação e dados devolvidos como JSON em vez de HTML montado à mão.

Em resumo

Perguntar à IA

Monte um prompt pronto sobre Validação de entrada e injeção (SQL, XSS, comandos) na sua linguagem e no seu caso, e abra num chat gratuito.

ChatGPT Claude Perplexity

O prompt vai para o site que você escolher. Não cole senhas, tokens nem dados reais de clientes. Para Gemini e outros, copie e cole.

Referências

Continue aprendendo

Todos os temas de boas práticas e padrões →