FlawOpen

flawopen.com/SQL Injection en Java/MyBatis

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

Página de referencia
Respuesta breve

Piensa en #{value} como un campo protegido de una instrucción preparada: la base de datos recibe datos. ${value} copia texto al SQL antes de prepararlo.

Valores vinculados y texto SQL sin procesar

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 qué MyBatis ofrece ambas sintaxis

#{} vincula valores con un parámetro JDBC PreparedStatement. Un parámetro no representa sintaxis SQL como tabla, columna u ordenación. ${} sustituye texto directamente y no lo escapa. Prefiere sentencias estáticas o ramas <choose>. Si hace falta, elige el identificador o fragmento desde una lista permitida del servidor; no interpoles texto arbitrario de la solicitud.

Cuándo se necesitan identificadores dinámicos

Permite el identificador o fragmento SQL completo

Un parámetro no sustituye identificadores. Para ordenar, asigna opciones a fragmentos SQL fijos del servidor (o usa <choose>). No pases columna, tabla ni ASC/DESC del cliente directamente a ${}. Escapar no vuelve seguro un fragmento arbitrario.

Cómo auditar mappers MyBatis

rg -n -F '${' --glob '*.xml' --glob '*.java' .
Revisa cada coincidencia en XML y anotaciones y rastrea el origen. Usa #{} para valores; para ${}, exige una lista finita del servidor. La búsqueda encuentra sintaxis, no toda construcción indirecta de SQL.

Preguntas frecuentes

¿También se aplica a mappers con anotaciones?

Sí. Se aplican las mismas reglas. ${column} en @Select inserta texto sin procesar; vincula valores con #{value} y limita identificadores con una lista.

¿Buscar ${} basta para demostrar que el código es seguro?

No. Revisa los resultados; la búsqueda puede omitir SQL ensamblado en otro sitio. Añade comprobaciones para XML y anotaciones y prueba que las opciones de ordenación usen fragmentos fijos esperados.

Referencias

Véase también Inyección SQL (CWE-89): cómo funciona y cómo evitarla en cada lenguaje — flawopen.com MySQLi vs PDO: prepared statements seguros ¿NHibernate protege frente a SQL Injection? Inyección NoSQL en MongoDB: filtros seguros SQL Injection en IDs numéricos: ¿riesgo? ¿Qué es una consulta parametrizada? ¿Prisma $queryRaw es seguro frente a SQL Injection?