A05 Injection
Quando dados e instruções viajam na mesma string, quem controla os dados controla a instrução. A cura é mantê-los separados, em qualquer linguagem.
O que é
A injection acontece quando uma aplicação monta um comando ou consulta concatenando entrada não confiável, e um interpretador (um motor SQL, um shell, um servidor LDAP, um template engine, um navegador) não consegue distinguir onde terminam os dados e começa a instrução.
O A05 no :2025 cobre injection de SQL, NoSQL, comandos do SO, LDAP e templates, e (XSS), que é injection no interpretador de HTML e JavaScript do navegador.
Como o ataque funciona
Uma consulta de login é montada como "SELECT * FROM users WHERE name = '" + name + "' AND pass = '" + pass + "'". Digitar admin' -- como nome transforma o resto da consulta em comentário e a verificação da senha desaparece. Com um UNION o atacante lê outras tabelas; com consultas empilhadas, escreve nelas.
- : ler, modificar ou apagar dados; às vezes alcançar o sistema operacional.
- Injection de comandos do SO: a entrada chega a um shell (; cat /etc/passwd, $(...), crases).
- XSS: a entrada chega à página sem escape e executa script na sessão de outro usuário.
- A injection de NoSQL e de templates segue o mesmo padrão com sintaxe diferente.
Código vulnerável
Concatenação de strings em SQL e em um comando de shell.
def find_user(db, name):
query = "SELECT id, email FROM users WHERE name = '" + name + "'"
return db.execute(query).fetchall()
def ping(host):
return os.popen("ping -c 1 " + host).read() # host = "x; cat /etc/passwd"const rows = await db.query(`SELECT id, email FROM users WHERE name = '${req.query.name}'`);
exec(`ping -c 1 ${req.query.host}`, cb);String sql = "SELECT id, email FROM users WHERE name = '" + name + "'";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);A correção
Parametrize: envie a consulta e os valores separadamente, para que o banco de dados nunca interprete o valor como SQL. Para comandos, evite o shell por completo e passe um array de argumentos. Para HTML, use um template engine que faça escape por padrão. A validação (listas de permissão de formato e tamanho) é uma segunda camada, não a correção.
def find_user(db, name):
return db.execute("SELECT id, email FROM users WHERE name = ?", (name,)).fetchall()
HOST_RE = re.compile(r"^[A-Za-z0-9.-]{1,253}$")
def ping(host):
if not HOST_RE.fullmatch(host):
raise ValueError("invalid host")
return subprocess.run(["ping", "-c", "1", host], capture_output=True, text=True).stdoutconst { rows } = await db.query("SELECT id, email FROM users WHERE name = $1", [req.query.name]);
// sem shell: os argumentos são passados como array
execFile("ping", ["-c", "1", host], cb);String sql = "SELECT id, email FROM users WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
}rows, err := db.QueryContext(ctx, "SELECT id, email FROM users WHERE name = $1", name)<p>Hello, {{ user_name }}</p> {# com escape #}
<p>Hello, {{ user_name | safe }}</p> {# PERIGOSO: não faça isso com entrada do usuário #}Prove a correção
Escreva o ataque como um teste. Envie os payloads clássicos e verifique que são tratados como dados simples: a consulta não retorna nada em vez de tudo, o shell nunca é invocado e os caracteres de script voltam com escape. Mantenha esses testes para sempre; eles documentam a ameaça.
PAYLOADS = ["' OR '1'='1", "admin' --", "'; DROP TABLE users; --", "x' UNION SELECT password,1 FROM users --"]
@pytest.mark.parametrize("payload", PAYLOADS)
def test_sql_payloads_are_just_data(db, payload):
assert find_user(db, payload) == [] # nenhuma linha tem esse nome literal
assert db.execute("SELECT count(*) FROM users").fetchone()[0] > 0 # a tabela continua existindo
def test_ping_rejects_shell_metacharacters():
with pytest.raises(ValueError):
ping("example.com; cat /etc/passwd")
def test_html_is_escaped(client):
body = client.get("/hello?name=<script>alert(1)</script>").text
assert "<script>" not in body and "<script>" in body# detectar SQL concatenado e shell=True na revisão, em qualquer commit
semgrep --config p/owasp-top-ten --error src/Checklist
- Consultas parametrizadas ou um ORM confiável em toda chamada ao banco; nada de SQL montado com strings.
- Nunca passe entrada a um shell. Se precisar executar um programa, use um array de argumentos e uma lista de permissão.
- Faça escape por contexto na saída (HTML, atributo, URL, JavaScript); mantenha o auto-escape ligado; adicione uma Content-Security-Policy.
- Valide a entrada por lista de permissão (tipo, tamanho, formato) como .
- Rode a conta do banco com : sem DROP, sem acesso a arquivos, sem admin.
- Escaneie no CI ( para o padrão, para o comportamento em runtime) e mantenha os payloads de ataque como testes de regressão.
conexões no mapa
- A05 Injectionpode contornarA01 Broken Access Control
atualizado em 14/10/2026 · fontes para aprofundar · reportar erro
em outros domínios
- Testes de segurança de web e APIoffseccomo testar o que o outro ensina a corrigir
- SASTappsecé o padrão que a análise estática procura no código
- Ataques L7: o que procurarsecopsvisto de fora: as assinaturas na borda
teste o que entendeu
Carregando perguntas…