Injeção: quando a entrada vira código
Injeção é o bug sério mais antigo e ainda o mais comum. Acontece quando dados de um usuário são tratados como instruções em vez de dados. Duas variantes importam para a maioria dos apps.
Injeção de — SQL (Structured Query Language, a linguagem que os apps usam para pedir dados a um banco de dados) — acontece quando a entrada do usuário se infiltra em uma consulta ao banco. O padrão vulnerável clássico:
// VULNERÁVEL: a entrada do usuário é colada direto na consulta
app.get("/user", (req, res) => {
const name = req.query.name;
db.query(`SELECT * FROM users WHERE name = '${name}'`);
});
// Se alguém passar name = '; DROP TABLE users; --
// sua consulta vira um comando para apagar a tabela.
A correção é uma consulta parametrizada (também chamada de prepared statement). Você envia a consulta e os dados separadamente, então o banco nunca confunde um com o outro:
// SEGURO: o valor é passado como parâmetro, nunca como código
app.get("/user", (req, res) => {
const name = req.query.name;
db.query("SELECT * FROM users WHERE name = ?", [name]);
});
A regra: nunca monte uma consulta por concatenação de strings. Se você ver crases ou + colando SQL em torno de uma variável em um diff, pare e peça a versão parametrizada.
Os dois caminhos colocam a mesma entrada do usuário no banco de dados, mas só um impede que o banco confunda essa entrada com comandos:
entrada do usuário: '; DROP TABLE users; --
│
├─────────────────────────┬───────────────────────────┐
▼ ▼
✗ CONCATENADA ✓ PARAMETRIZADA
"...WHERE name= "...WHERE name = ?", [entrada]
'" + entrada + "'" (consulta e dados enviados separados)
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ BANCO DE │ │ BANCO DE │
│ DADOS │ │ DADOS │
│ lê a entrada │ │ trata a │
│ COMO COMANDO │ │ entrada só │
│ → tabela some│ │ como DADO │
└───────────────┘ └───────────────┘
VIOLAÇÃO SEGURO
XSS (cross-site scripting) é a mesma ideia no navegador. Se você pega a entrada do usuário e a coloca em uma página como HTML bruto (HyperText Markup Language, o código que diz ao navegador o que exibir), um atacante pode injetar uma tag <script> que roda no navegador dos seus outros usuários — roubando as sessões deles, por exemplo. A correção é o escaping (transformar caracteres especiais em texto seguro): renderizar o conteúdo do usuário como texto, não como HTML. Frameworks modernos como o React fazem escaping por padrão, o que é ótimo, até a IA recorrer a uma saída de emergência como dangerouslySetInnerHTML ou innerHTML para "fazer funcionar". Isso contorna a proteção. Trate qualquer chamada desse tipo em um diff como algo a questionar.
O princípio unificador por trás de todo bug de injeção: mantenha dados e código separados. Sempre que a entrada do usuário cruza para dentro de uma consulta, um comando, um template ou HTML, algo precisa fazer escaping ou parametrizar.