JPA SQL Injection es un problema del que muchos desarrolladores se olvidan ,ya que consideran que JPA esta totalmente protegido contra inyeccion de SQL. ¿Es esto cierto o podemos vernos afectados?. La realidad es que no estamos totalmente protegidos . Depende mucho de como se haya construido el código . Vamos a ver un ejemplo sencillo que nos ayude a entenderlo. Supongamos que tenemos una base de datos con los siguientes datos.
Vamos a construirnos un ejemplo con JPA que nos permita seleccionar datos de la tabla. El primer paso es definir las dependencias de Maven que vamos a utilizar.
<dependencies> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>1.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.10.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.42</version> </dependency> </dependencies><dependencies> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>1.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.10.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.42</version> </dependency> </dependencies>
El siguiente paso es definir la clase de Entidad que vamos a utilizar:
package com.arquitecturajava; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Persona { @Id private String nombre; private String apellidos; private int edad; public String getNombre() { return nombre; } public void setNombre(String nombre) { this.nombre = nombre; } public String getApellidos() { return apellidos; } public void setApellidos(String apellidos) { this.apellidos = apellidos; } public int getEdad() { return edad; } public void setEdad(int edad) { this.edad = edad; } public Persona(String nombre, String apellidos, int edad) { super(); this.nombre = nombre; this.apellidos = apellidos; this.edad = edad; } public Persona() { super(); } }
Por último nos queda ver como queda el programa principal que va a realizar una consulta:
package com.arquitecturajava; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.TypedQuery; public class Principal { public static void main(String[] args) { seleccionarPersona("pedro", "perez"); } private static void seleccionarPersona(String nombre, String apellidos) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("UnidadPersonas"); EntityManager em = emf.createEntityManager(); try { String sql = "select p from Persona p "; if (nombre != null || apellidos != null) { sql += " where "; } if (nombre != null) { sql += " nombre='" + nombre + "'"; } if (apellidos != null) { if(nombre!=null) { sql += " and apellidos='" + apellidos + "'"; }else{ sql += " apellidos='" + apellidos + "'"; } } TypedQuery<Persona> consulta = em.createQuery(sql, Persona.class); List<Persona> lista = consulta.getResultList(); lista.forEach((p) -> { System.out.println(p.getNombre()); }); } catch (Exception e) { e.printStackTrace(); } finally { em.close(); } } }
JPA SQL Injection
El programa principal se encarga de realizar una consulta JPA utilizando SQL dinámico y construyendo la consulta por partes.
En este caso estamos buscando una persona por nombre y apellidos , así pues pasamos ambos parámetros. El resultado por la consola confirma que la consulta funciona de forma correcta y que solo el registro de pedro cumple.
Ahora bien estamos ante una situación comprometida ya que estamos construyendo la consulta de forma dinámica y nos pueden realizar un ataque de SQL Injection. ¿Cómo nos pueden atacar ? . Muy sencillo es suficiente con modificar el parámetro que recibimos de apellidos por algo como “‘ or “=” “.
Esta modificación añadirá a la SQL una condición que siempre se cumple (inyectandola).
seleccionarPersona("pedro", <strong>"' or ''='");</strong>
La nueva SQL generada será:
select persona0_.nombre as nombre1_0_, persona0_.apellidos as apellido2_0_, persona0_.edad as edad3_0_ from Persona persona0_ where persona0_.nombre=’pedro’ and persona0_.apellidos=” or ”=”
El resultado por pantalla cambia y nos muestra todos los usuarios de la tabla :
Tenemos un problema de JPA SQL Injection y hemos recibido un ataque. Para solventar estos casos deberemos utilizar el API de criteria que veremos en el próximo artículo.
Otros artículos relacionados:
Si seteamos los parámetros con .setParameter() no haria falta usar el API de Criteria
Consola:
id apellido nombre
1 Perez Pedro
2 Fernandez Juan
3 Rodriguez Gema
Muy interesante y gracias por la explicación.
Qué sucede si utilizando JPA pasamos los parámetros de esta forma:
sino que de esta:
Select p Persona p where nombre= :nombre and apellidos= :apellidos
y en consulta seteamos estos valores:
consulta.setParemeter(“nombre”, this.nombre);
consulta.setParemeter(“apellido”, this.apellido);
Sería el mismo concepto que se utiliza para JDBC y sus (?,?,?)
Saludos!
si es lo mismo 🙂