pular para o conteúdo
voltar ao mapa
técnica
etapa: code
3 min de leitura

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.

sqlmap (on systems you own)SemgrepCodeQLOWASP ZAPBurp SuiteORM / query builders

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.

users.pypython
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"
users.jsjavascript
const rows = await db.query(`SELECT id, email FROM users WHERE name = '${req.query.name}'`);
exec(`ping -c 1 ${req.query.host}`, cb);
UserDao.javajava
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.

users.pypython
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).stdout
users.jsjavascript
const { 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);
UserDao.javajava
String sql = "SELECT id, email FROM users WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, name);
    ResultSet rs = ps.executeQuery();
}
users.gogo
rows, err := db.QueryContext(ctx, "SELECT id, email FROM users WHERE name = $1", name)
XSS: faça escape na saída (o autoescape do Jinja2 vem ligado; nunca marque entrada do usuário como safe)html
<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.

test_injection.py (pytest)python
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 "&lt;script&gt;" in body
static-check.shbash
# 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.
OWASP Top 10A lista de referência mais famosa de riscos de segurança em aplicações web (A01 acesso quebrado, A02 falhas criptográficas, A03 injeção...), revisada a cada alguns anos pela comunidade OWASP. É um mapa educacional de categorias de risco — não um checklist de ferramenta nem uma certificação.ver no glossário →XSScross-site scriptingInjeção em uma página web: entrada não confiável é renderizada como HTML ou script no navegador de outro usuário. Faça escape na saída conforme o contexto e adicione uma Content-Security-Policy.ver no glossário →SQL injectionSQLiEntrada concatenada em uma string SQL é interpretada como parte da consulta. Consultas parametrizadas enviam os valores separadamente e eliminam o problema.ver no glossário →Defesa em profundidadeCamadas independentes de proteção, partindo da premissa de que qualquer uma pode falhar: validação + parametrização + WAF + monitoramento.ver no glossário →Privilégio mínimoleast privilegePrincípio de dar a cada identidade só as permissões estritamente necessárias para a sua função, e nada mais. É oposto a 'usuário admin por padrão': cada papel extra é um caminho a mais que um atacante pode usar depois de um compromise.ver no glossário →SASTAnálise estática: examina o código sem executar. Acha por análise de fluxo, não por adivinhação.ver no glossário →DASTTeste dinâmico: ataca a aplicação rodando, de fora, em caixa-preta.ver no glossário →

conexões no mapa

atualizado em 14/10/2026 · fontes para aprofundar · reportar erro

em outros domínios

teste o que entendeu

Carregando perguntas…