FlawOpen

flawopen.com/SQL Injection em Java/MyBatis

MyBatis ${} vs #{}: evitar SQL Injection

Página de referência
Resposta curta

Pense em #{value} como um campo protegido numa instrução preparada: o banco trata a entrada como dado. ${value} copia texto para o SQL antes da preparação.

Valores vinculados e texto SQL bruto

INSEGURO — texto bruto ${}
<select id="getUser">
  SELECT * FROM users
  WHERE id = ${id}
</select>
// Raw substitution: the SQL text changes
VALOR VINCULADO — #{}
<select id="getUser">
  SELECT * FROM users
  WHERE id = #{id}
</select>
// MyBatis creates a PreparedStatement parameter

Por que o MyBatis oferece as duas sintaxes

#{} vincula valores por parâmetro JDBC PreparedStatement. Parâmetros não representam sintaxe SQL, como tabela, coluna ou direção de ordenação. ${} substitui texto diretamente e não o escapa. Prefira instruções estáticas ou ramos <choose>. Quando necessário, selecione o identificador ou fragmento completo de uma lista permitida no servidor; nunca interpole texto arbitrário da requisição.

Quando identificadores dinâmicos forem necessários

Permita o identificador ou fragmento SQL completo

Parâmetros não substituem identificadores. Para ordenação, mapeie opções para fragmentos SQL fixos no servidor (ou use <choose>). Não passe coluna, tabela ou ASC/DESC do cliente diretamente a ${}. Escapar ou citar não torna fragmentos arbitrários seguros.

Como auditar mappers MyBatis

rg -n -F '${' --glob '*.xml' --glob '*.java' .
Revise cada ocorrência em XML e anotações; rastreie a origem do valor. Use #{} para valores; para ${}, exija uma lista finita no servidor. Busca textual encontra sintaxe, mas não toda montagem indireta de SQL.

Perguntas frequentes

Isso vale para mappers com anotações?

Sim. Anotações usam as mesmas regras. ${column} em @Select insere texto bruto; vincule valores com #{value} e permita identificadores por lista.

Buscar ${} basta para provar que o código é seguro?

Não. Revise os resultados; a busca pode não encontrar SQL montado em outro lugar. Adicione verificações para XML e anotações e teste se escolhas de ordenação usam apenas fragmentos fixos esperados.

Referências

Veja também SQL Injection (CWE-89): como funciona e como evitar em todas as linguagens — flawopen.com MySQLi vs PDO: prepared statements seguros NHibernate protege contra SQL Injection? MongoDB NoSQL Injection: filtros de consulta seguros String de IDs numéricos também pode causar SQL Injection? O que é uma consulta parametrizada? Prisma $queryRaw: é seguro contra SQL Injection?