Search code examples
spring-bootjpasql-injectionjpql

Prevent JPQL query sql injection


I was advised that below query is not safe as parameter :searchFor coming from input field in front end can be used for SQL injection. Plase advise what is the best solution to prevent SQL injection in below code?

@Query("SELECT u FROM User u WHERE lower(u.username) LIKE %:searchFor% " +
        " OR lower(concat(u.firstname, ' ', u.lastname)) LIKE %:searchFor% " +
        " OR lower(u.email) LIKE %:searchFor%")
Page<User> findAllAndSearch(@Param(value = "searchFor") String searchFor, Pageable pageable);

I am not using "+" to join strings, but provide parameter instead (:searchFor). Not sure if this still unsafe.


Solution

  • I was advised that below query is not safe as parameter :searchFor

    You should challenge this advise.

    SQL injections occur when the parameter value passed by the client may transmit additional query logic (generally undesirable) and that that will be allowed in the executed query such as.
    For example instead of a simple foo textual value for searchFor, the parameter value could contain also additional logic in the query, for example : foo OR ''=''. That is SQL injection.

    In your case, SQL injection is not possible because you don't set the parameter manually but rather you rely on a safe way to bind thesearchFor parameter : Spring.
    Indeed, Spring binds the parameter values in a safe way as JPA implementations do, that is setting the parameter value from a JPA Query instance that is protected against SQL injections for declared parameters.

    For example take this query (I removed the % part to simplify) :

    "SELECT u FROM User u WHERE lower(u.username) LIKE :searchFor"
    

    And try to set the param of searchFor with the String "foo OR ''==''" to try to inject a SQL condition that is always true.
    If you turn on the log of your JPA implementation to output the paramaters binding (for Hibernate : logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE) you could see something like :

    TRACE 11012 --- [nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [foo OR ''=='']

    The binding is only performed for the parameter value and not as a way to add new query logic. The final query part protected is so protected as :

    SELECT u FROM User u WHERE lower(u.username) LIKE "foo OR ''==''"