flawopen.com/SQL Injection em Java/MyBatis
MyBatis ${} vs #{}: evitar SQL Injection
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
<select id="getUser">
SELECT * FROM users
WHERE id = ${id}
</select>
// Raw substitution: the SQL text changes
<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
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' .Perguntas frequentes
Sim. Anotações usam as mesmas regras. ${column} em @Select insere texto bruto; vincule valores com #{value} e permita identificadores por lista.
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.