Prevent JPQL query sql injection - spring-boot

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.

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 ''==''"

I believe the underlying ORM framework would sanitise input if passed as parameter this way.

Related

Getting Second Order SQL Injection in Spring Hibernate

I am facing Second Order SQL Injection in the Spring-Hibernate application after scanning through the Checkmarx tool, I have gone through multiple questions in StackOverflow and in other platforms as well but did not get the right finding.
could you please look into the below code snip,
public String getOrderId(order_name){
String returnId= null;
Query query = entityManager.createNativeQuery("select order_id from order where order_name=?");
List<String> dataset = query.setParameter(1,order_name).getResultList();
if(dataset!=null){
returnId = dataset. Get(0);
}
return returnId;
}
In this above method, while calling getResultList(), getting a high vulnerability issue that, this method returns data flows through the code without being properly sanitized or validated, and eventually used in further database query in the method.
Earlier code was like this,
public String getOrderId(order_name){
String returnId= null;
String q = "select order_id from order where order_name="+order_name;
Query query = entityManager.createNativeQuery(q);
and directly it was used as a string append in query, which I have modified with set parameter,
Query query = entityManager.createNativeQuery("select order_id from order where order_name=?");
List<String> dataset = query.setParameter(1,order_name).getResultList();
but still after getting data from query.getResultSet(), it is asking for sanitizing and validating the data before use in further database query method.
and this return data is being used in further query like select * from return_Data where clause. (properly used in where clause to set parameter to avoid SQL injection).
and in the above query is used in another method where we pass return_Data as input to it.
could you please help here to know what checks and validation can be added to overcome this type of issue. Thanks in advance for prompt response.

Bind values to search String into table column

I'm trying to implement a search functionality into table column using this code:
#Query(
value = "SELECT * " +
"FROM ACTIVE_PAIRS ap " +
"WHERE ap.pair iLIKE CONCAT('%', ?1, '%') " +
"LIMIT ?2 " +
"OFFSET ?3"
)
Flux<ActivePairsFullDTO> findAllBySearchParam(String params, long limit, long offset);
But I get error: Binding parameters is not supported for the statement
Do you know how I can fix this issue?
I'm a little surprised that you are getting this error, because I would have thought the first error you should get is that your statement is not valid JPQL, nor HQL since LIMIT and OFFSET aren't part of JPQL.
In order to use SQL you need to specify nativeQuery=true in the annotation.
Since you also return a Flux I'm not sure you use Spring Data JPA as tagged, but Spring Data R2DBC?
Either way, the exception you are seeing stems from the fact that some databases don't accept bind variables for LIMIT and OFFSET.
For Spring Data JPA you can work around this by simply removing those clauses and add a PageRequest argument to the method.
This approach is not supported for Spring Data R2DBC. Instead you would use SpEL expressions to include the LIMIT and OFFSET values.

Spring And Kotlin Query

How can I achieve this query
select *
from table t
where name like '%Ami%'
order by (name = 'Ami') desc, length(col);
(just the sort part)
Using springframework Sort..
What I tried is
Sort.by(Sort.Direction.DESC, "name") // But I need to sort by name = 'Ami'
Sort.by(Sort.Direction.DESC, "name" = 'Ami'") // throws an error
JpaSort.unsafe(Sort.Direction.DESC, "name" = 'Ami'") // throws an error
Looks like the documentation has an example almost identical to your question:
https://docs.spring.io/spring-data/jpa/docs/2.4.5/reference/html/#jpa.query-methods.sorting
However, using Sort together with #Query lets you sneak in
non-path-checked Order instances containing functions within the ORDER
BY clause. This is possible because the Order is appended to the given
query string. By default, Spring Data JPA rejects any Order instance
containing function calls, but you can use JpaSort.unsafe to add
potentially unsafe ordering.

Attempting to prevent SQL injection when referencing an Oracle Package dynamically with JPA

I've gone down a bit of a path and hit a wall with how this could be possibly achieved.
Basically, a query is constructed using JPA and passed to an Oracle DB. On the DB there is a Package, used to generate a reference, and this is dynamically named, based on the environment. This value is user-editable, and stored as a DB property within the application. I don't have any control over the architecture of this.
At a pre-JPA stage, a Query String is generated using the reference value for the Package, which is set as a property (again, I can't change the way this has been designed). I set this up using the Query method setParameter(), like so:
(pseudocode replacing the irrelevant parts for focused context)
String referenceRef = [ reference is fetched from DB properties ];
String queryString = "SELECT ?1 FROM sys.dual";
final Query myQuery = getEntityManager().createNativeQuery( queryString );
myQuery.setParameter( 1, referenceRef );
return myQuery.getSingleResult();
I pretty much did this as a reflex, only to realise (in retrospec, quite obviously) that this won't actually work, as it is escaping the element that should not be escaped...
So, where the referenceRef = "DynamicallyNamedPackage.DoThisDynamicallyNamedThing", the above code will just return "DynamicallyNamedPackage.DoThisDynamicallyNamedThing", as it is obviously making it safe, and the point of doing so is, to a certain extent, the antethesis of what I'm trying to do.
Is it possible to achieve this without creating a whole chunk of additional code? All I can currently think of, as an alternative, is to query dba_procedures for all package objects that match, and using the result of that query to construct the queryString (hence circumnavigating using any user-editable values), but it feels like it's going to be convoluted. This is the alternative, which I am using in lieu of an improvement:
final String verifyReference = "SELECT object_name FROM "
+ "dba_procedures WHERE object_type = 'PACKAGE' AND object_name =?1";
final Query refQuery = getEntityManager().createNativeQuery( verifyReference );
refQuery.setParameter( 1, referenceRef );
final String result = refQuery.getSingleResult();
final String queryString = "SELECT " + result + " FROM sys.dual";
final Query myQuery = getEntityManager().createNativeQuery( queryString );
return myQuery.getSingleResult();
It will essentially look up the user-editable property reference against a list of existing packages, then use the result of that query for building the original reference. It has more null checking and so on involved, and does remove the vulnerability, but feels a bit 'unpolished'.
(As has already been mentioned in the comments, this sort of is designed to need a SQL injection, but needs to prevent "SQL Injection" as a definition of not allowing the DB to be manipulated outside of the design by using an unintended value.)
The Oracle dictionary view all_procedures contains a list of all procedures accessible to the current user.
Specifically in the view there are columns OWNER, OBJECT_NAME (=package name), PROCEDURE_NAME.
You may use this view to sanitize the configured input by simple adding an EXISTS subquery such as:
select
?
from dual where exists (
select null from all_procedures
where
OWNER||'.'||OBJECT_NAME||'.'||PROCEDURE_NAME = upper(?) and
object_type = 'PACKAGE');
You will have to bind twice the same input parameter.
The query returns no data if there is not procedure with the given name, so you may raise an exception.
The query above expects a full qualified stored procedure name, i.e. owner.package.procedure, you'll have to adapt it slightly if you allow unqualified names (without the owner).

Using Postgres JSONB query with Spring Data and bind parameter fails with InvalidDataAccessApiUsageException

I am currently looking for a solution for the exception
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter with that position [1] did not exist;
My current #Query annotation is:
#Query(
nativeQuery = true,
value = "SELECT * FROM thgcop_order_placement WHERE \"order_info\" #> '{\"parentOrderNumber\":\" :param \"}'")
I guess the position [1] did not exist comes from it being in double quotes plus double quote plus single quote.
How can I make this work?
The query is using Postgres JSONB datatype. The column definition is ORDER_INFO JSONB
The following native query works just fine in the Postgres client:
SELECT * FROM thgcop_order_placement
WHERE "order_info" #> '{"parentOrderNumber":"ORD123"}'
None of the above worked for me except the below,
Service Layer code :-
OrderInfo orderInfo = new OrderInfo();
orderInfo.setParentOrderNumber("ORD123");
....
String param = objectMapper.writeValueAsString(orderInfo);
List<Order> list = jpaRepository.getByParentOrderNumber(param);
JpaRepository.java code :-
#Query(nativeQuery = true, value = "select * from thgcop_order_placement where order_info #> CAST(:condition as jsonb)")
List<Order> getByParentOrderNumber(#Param("condition") String parentOrderNumber);
This is how I achieve the result. I hope this will be very helpful for all enthusiastic!!
Thank you all for your help !!!
TL;DR: Make it work with Bind Parameter and plain JDBC first. Then move on to Spring Data, possibly falling back on a custom implementation.
You are facing problems on many levels here.
Let's start with ignoring Spring Data for now.
The statement you showed is very dissimilar from the one you try to construct with Spring Data because it doesn't contain a bind variable.
So instead of
SELECT * FROM thgcop_order_placement WHERE "order_info" #> '{"parentOrderNumber":"ORD123"}'
We should compare it to
SELECT * FROM thgcop_order_placement WHERE "order_info" #> '{"parentOrderNumber": ? }'
Note that we are losing the quotes since they denote a literal String but we aren't providing a literal String but a bind parameter.
I haven't found any indication that you can use bind parameters in parts of JSON expressions. So instead of the statement above we would need to use:
SELECT * FROM thgcop_order_placement WHERE "order_info" #> ?
Of course, the bind parameter should then contain the complete JSON expression
Unfortunately, this doesn't seem to work either because now Postgres considers the bind parameter a VARCHAR instead of a JSON expression. See https://blog.2ndquadrant.com/processing-json/. I think the correct version should be
SELECT * FROM thgcop_order_placement WHERE "order_info" #> ?::json
But I couldn't get this to work either.
In any case, you are left to transform your parameter to the JSON structure.
Normally I'd suggest using a SpEL expression for this. But it won't work because Spring Data chokes on the curly braces needed in the SpEL expression and considers them the end of the SpEL expression.
If you get something like this to work with a simple JDBC connection or JdbcTemplate you can start to think about #Query annotations.
#Query(
value= "SELECT * FROM thgcop_order_placement WHERE \"order_info #> :name::json",
nativeQuery = true)
This might trigger more problems since Spring Data will either consider ::json part of the parameter name. If this is the case you'll have to fall back on custom implementations.
I ran a couple of experiments, which you can look at and play with here.
Try to bind parameters as following
#Query(nativeQuery = true, value = "SELECT * FROM thgcop_order_placement"
+ " WHERE \"order_info\" #> '{\"parentOrderNumber\":\" ?1 \"}'")
I was stuck at the same problem for a while as well. It seems that springboot messes up while parsing the query present in string format. However this is the solution that I found, which will work directly as a native query in the repository:
Since order_info is of the type jsonb, the value being searched can be casted as a jsonb value.
Value to be searched: {"parentOrderNumber":"ORD123"}
Let's escape the whole string to be parsed by java.
String searchString = "{\"parentOrderNumber\":\"ORD123\"}"
Now, let's type the postgres query in a manner that spring will understand.
#Query(
value = "SELECT * from thgcop_order_placement where ((?1\\:\\:jsonb) <# (order_info\\:\\:jsonb))",
nativeQuery=true
)
List<Order> getByParentOrderNumber(String searchString);
Where,
Spring will replace ?1 with the value of searchString, as defined above.
:: is the typecast operator using which, we are explicitly typecasting the passed parameter (searchString) into jsonb. Therefore the value {\"parentOrderNumber\":\"ORD123\"} is converted into jsonb before an attempt is made for a search.
Also the values of column order_info is explicitly typecasted into jsonb.
Now, when both items (value to be searched and the column) are the same data type, we can use the <# operator to check if the search string is contained in the column values.
At the service level we just have to do this:
String orderNumber = "-- some order value e.g. ORD123 --"
String searchString = "{\"parentOrderNumber\":\"" + orderNumber + "\"}"
List<Order> list = jpaRepository.getByParentOrderNumber(searchString);
More details on Postgres JSON operators can be found in the official documentation HERE: https://www.postgresql.org/docs/9.5/functions-json.html

Resources