Avoid SQL Injection in Spring JPA - java

I am using sql query like
String query = "Select max(case when UPPER(key)='firstname' then value end) as firstNameName, , ... order by "+orderBy;
result = em.createNativeQuery(query).getResultList();
em.close();
for some reason i would have to use dynamic +orderBy . Where orderBy =firstname ASC , lastname DESC etc. Tried using .setParameter(1, orderedBy) but in this case i am not getting expected ordered results.

For avoiding sql injection threats you firstly need to remove appending parameters to your query. When you're appending parameters in your app, the atacker can hijack your sql code (with apostrophes and other means for example)
For example:
If your query is "select * from table where name="+id
The attacker can pass to the field values such as:
'John' or 1=1; ->sees all your records in this table
Or even
'John' or 1=1;Delete all from users;' -> deleting all entries from users table.
Hijacking queries can be avoided via mechanisms such as input sanitization, input whitelisting/blacklisting(removing unwanted characters from the input/ defining a list of allowed or unnalowed characters). Most modern framerowks such as JDBC/JPA/Hibernate can offer protection from this threat.
With all this stated we should take into consideration the following scenarios:
Considering sql where parameters:
JDBC for example offers prepared statements, where you define a variable in your sql, and the framework replaces it
In your case a JPA implementation(Hibernate) also has mechanisms for avoiding this threat, also via parameterized queries and positional paramaters:
via native query positional parameters:
Query q = em.createNativeQuery("SELECT count(*) FROM mytable where username = ?1");
q.setParameter(1, "test");
via named parameters(jplq)
Query q = em.createQuery("SELECT count(*) FROM mytable where username = :username");
q.setParameter("username", "test");
Considering orderBy parameters:
CriteriaQuery/spring Specifications
CriteriaBuilder cb = this.entityManager
.getCriteriaBuilder();
CriteriaQuery<RESULT> criteria = cb.createQuery(RESULT.class);
Root<RESULT> root = criteria.from(RESULT.class);
return this.entityManager.createQuery(
criteria.select(root).orderBy(cb.asc(root.get("ORDER_BY_FIELD"))))
.getResultList();
More on criteriaQueries usage and config here
Passing orderBy via spring specific Sort parameter built beforehand(using the spring-data library)
Sort sort = Sort.by(Sort.Direction.ASC, "criteria");
em.createQuery(QueryUtils.applySorting(yourSqlQuery_withoutSorting,sort));
annotate method with #Query with spring-data library:
You can achieve the same result with less boiler plate code(without injecting an entityManager and creating a nativeQuery) by just annotating a method with a #Query annotation:
#Query("select u from User u where u.lastname like ?1%")
List<User> findByAndSort(String lastname, Sort sort);
Note:
Native vs non-native(jpql):
JOQL queries are independent of the database vendor(MySQL,PostGres,Oracle,DB2), nativeQueries usages are more focused when you need to use a database specific functionalities which differes accross vendors.
For a brief example jpql can not support native [With]2 clause PLSQL standard functions
Regarding your edit:
You can try to apply the following sql trick for dynamic ordering:
SELECT param1, param2 ...
FROM ...
ORDER BY case when :sortParam='name asc' then name asc END
case when :sortParam='name desc' then name desc END
....
else 0

Related

Passing entire where clause to native spring jpa #Query [duplicate]

This question already has answers here:
Dynamic spring data jpa repository query with arbitrary AND clauses
(4 answers)
Closed 3 years ago.
Currently I'm unable to know the fields that will be within the SQL where clause (think adhoc) when creating the native SQL in my spring #Query. Therefore I'm passing in the entire where clause.
If I output the SQL to the console and paste it in to my sql editor I'm able to receive a valid resultset.
SELECT * FROM lorder WHERE order_id = 1196077
Last SQL output to the console was:
Hibernate:
/* dynamic native SQL query */ SELECT
*
FROM
lorder
WHERE
?
and the where clause value being passed in to the #query is:
order_id = 1196077
This is what I am currently doing which is not working.
#Query(
value = "SELECT * FROM lorder WHERE :where",
nativeQuery = true)
List<OrderEntity> listSelected(#Param("where") String where);
Not sure if passing the entire where clause is possible but I'm expecting a list. However I'm currently getting an empty collection.
Since using the #Query annotation you can only use named parameters (your :where) or ordinal parameters (e.g. ?1, ?2) of a specific Java-type, it's not possible to inject partial SQL-expressions.
However you could use a TypedQuery to add partial SQL to a query:
public List<OrderEntity> getOrdersUsingWhereClause(EntityManager em, String whereClause) {
TypedQuery<OrderEntity> query = em.createQuery(
"SELECT o FROM lorders o WHERE " + whereClause,
OrderEntity.class);
return query.getResultList();
}
See https://www.objectdb.com/java/jpa/query/parameter#Parameters_vs.Literals
The JPA's criteria query gives you more power to control where clause along with many other supported functionalities.
List of functions : https://en.wikibooks.org/wiki/Java_Persistence/Criteria
Criteria Queries : https://www.baeldung.com/hibernate-criteria-queries
Learn more about criteria query and criteria builder here

How can I correctly implement an Hibernate SQL query starting from an SQL query that count the number of rows?

I am absolutly new in Hibernate and I have the following problem.
I have this standard SQL query:
SELECT count(*)
FROM TID003_ANAGEDIFICIO anagraficaEdificio
INNER JOIN TID002_CANDIDATURA candidatura
ON (candidatura.PRG_PAR = anagraficaEdificio.PRG_PAR AND candidatura.PRG_CAN = anagraficaEdificio.PRG_CAN)
INNER JOIN TID001_ANAGPARTECIPA anagPartecipa ON(anagPartecipa.PRG_PAR = candidatura.PRG_PAR)
INNER JOIN anagrafiche.TPG1029_PROVNUOIST provNuovIst ON (provNuovIst.COD_PRV_NIS = anagPartecipa.COD_PRV_NIS)
WHERE anagraficaEdificio.FLG_GRA = 1 AND provNuovIst.COD_REG = "SI";
This works fine and return an integer number.
The important thing to know is that in this query the only
parameter that can change (inserted by the user in the frontend of a webappplication) is the last one (this one: provNuovIst.COD_REG = "SI").
So, the application on which I am working use Hibernate and the requirement say that I have to implement this query using Hibernate Native SQL, I have found this tutorial:
http://www.tutorialspoint.com/hibernate/hibernate_native_sql.htm
that show this example:
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
query.setParameter("employee_id", 10);
List results = query.list();
that, from what I have understand (correct me if I am doing wrong assertion), involves the use of an Employee model class. So th prvious query first define the query (using the :param_name syntax for the parameter), then create an SQLQuery Hibernate object, add the class used for the result, set the previous parameter neam and finally obtain a List (that I think Hibernate create as something like an ArrayList) with the retrieved object.
My problem is that I simply I have to obtain an integer value (because I have a SELECT count(*), so I will obtain an integer value and not a set of rows).
So how can I correctly use the Hibernate Native SQL to implement my SQL query into my Hibernate repository class?
Use SQLQuery.uniqueResult to retrieve a single value from the query:
String sql = "SELECT count(*) ...";
SQLQuery query = session.createSQLQuery(sql);
// set parameters...
int count = ((Number)query.uniqueResult()).intValue();

Get SQL String substituted with parameters using java

Is there any easy way to get a completed SQL statement after parameter substitution?
I am using elasticsearch-sql to query elastic search with sql statements, however I have to submit the query with all the parameters substituted.
I tried Hibernate Query getQueryString, but the parameter substitution is not happening for those sql strings.
The following sql string is produced:
"SELECT * FROM USER WHERE NAME=? AND SURNAME=?"
rather than:
"SELECT * FROM USER WHERE NAME='Selva' AND SURNAME='Esra'
Appreciate any better idea/thoughts?
1. Named parameters
This is the most common and user friendly way. It use colon followed by a parameter name (:example) to define a named parameter. See examples…
String hql = "SELECT * FROM USER WHERE NAME= :userName AND SURNAME= :surName";
Query query = session.createQuery(hql);
query.setParameter("userName ", "userName");
query.setParameter("surName", "SurName");
List results = query.list();
An object-oriented representation of a Hibernate query. A Query instance is obtained by calling Session.createQuery(). This interface exposes some extra functionality beyond that provided by Session.iterate() and Session.find():
a particular page of the result set may be selected by calling setMaxResults(), setFirstResult()
named query parameters may be used
the results may be returned as an instance of ScrollableResults
Named query parameters are tokens of the form :name in the query string. A value is bound to the integer parameter :foo by calling
setParameter("foo", foo, Hibernate.INTEGER);
for example. A name may appear multiple times in the query string.
JDBC-style ? parameters are also supported. To bind a value to a JDBC-style parameter use a set method that accepts an int positional argument (numbered from zero, contrary to JDBC).
You may not mix and match JDBC-style parameters and named parameters in the same query.
2. Positional parameters
It’s use question mark (?) to define a named parameter, and you have to set your parameter according to the position sequence. See example…
Java
String hql = "from Stock s where s.stockCode = ? and s.stockName = ?";
List result = session.createQuery(hql)
.setString(0, "7277")
.setParameter(1, "DIALOG")
.list();
This approach is not support the setProperties function. In addition, it’s vulnerable to easy breakage because every change of the position of the bind parameters requires a change to the parameter binding code.
Java
String hql = "from Stock s where s.stockName = ? and s.stockCode = ?";
List result = session.createQuery(hql)
.setParameter(0, "DIALOG")
.setString(1, "7277")
.list();
Conclusion
In Hibernate parameter binding, i would recommend always go for “Named parameters“, as it’s more easy to maintain, and the compiled SQL statement can be reuse (if only bind parameters change) to increase the performance.

How to execute a JPA Bulk Update statement which takes a List as a parameter value

I have an Update Query that looks like this
UPDATE
table_name
SET
column_name = ?
WHERE
column_name = ? AND id in (?)
So the JPA transaction is
em.createNativeQuery(Update_QUERY)
.setParameter(1, updatedStatus)
.setParameter(2, currentStatus)
.setParameter(3, ids)
.executeUpdate();
The Input to the method is List id, currentStatus, and updatedStatus
How do I pass the List as a single parameter, if I convert the List to a comma-separated String I get the error Specified text is not number as strings is not allowed in the In clause.
How do I pass the List as a single parameter
An example approach:
String jpql = "UPDATE NameEntity ne " +
"SET ne.valstring = :updated_status " +
"WHERE ne.valstring = :current_status AND ne.id IN :ids";
em.createQuery(jqpl)
.setParameter("updated_status", updatedStatus)
.setParameter("current_status", currentstatus)
.setParameter("ids", Arrays.asList(ids))
.executeUpdate();
Three simple rules:
Use native SQL for bulk update / delete on tables that are not mapped to entities.
Native SQL queries work directly on database tables bypassing the persistence context (a set of managed entities), so it is safe to use such queries if a given database table has no corresponding entity.
Use JPQL for bulk update / delete on tables that are mapped to entities
In case of a given database table is mapped by an entity, using a SQL update / delete will lead to inconsistency between persistence context and the underlying database, so use JQPL counterparts instead and the persistence provider will take care of consistency.
Bulk update / delete should be executed as the first operation within the transaction or ideally in its own transaction.
Setting a List parameter
The JPA Query interface setParameter method that accepts an Object parameter:
Query setParameter(String name, Object value)
can take a List as the parameter value.
This works in the same way for JPQL, Criteria API, or bulk update and delete queries:
List<Post> posts = entityManager.createNativeQuery("""
UPDATE
post
SET
status = :newStatus
WHERE
status = :oldStatus AND
id IN :ids
""", Post.class)
.setParameter("oldStatus", PostStatus.PENDING)
.setParameter("newStatus", PostStatus.APPROVED)
.setParameter("ids", List.of(1L, 2L, 3L))
.executeUpdate();
For more details about executing bulk update and delete statements with JPA and Hibernate, check out this article as well.

jpa select statement with subnets

I am trying to write query with multiple select subnets in it.But I defined a nativequery
I am giving error. Compiler specifies that "(" after "from" is not proper. How can I define
a native query in JPA 2.0
For eaxmple:
SELECT *
from (SELECT ****C) REI3 where column1 != 1
GROUP BY REI3.column2 order by REI3.column3 ASC
JPA does not have too much to do with validating SQL syntax, query is passed to JDBC driver. Likely you are trying run query such a way, that it is interpreted as JP QL. Instead try following method to execute it as
Query q = em.createNativeQuery("Your SQL here");
Other alternative is to use NamedNativeQuery Example

Categories

Resources