We are using Hibernate on Tomcat. We recently found a SQL injection vulnerability in our Hibernate code. Specifically in reguards to the POJOs we use for ORM.
We are taking in the user input and creating a new POJO like this:
//POJO associated with Foo.hib.xml
public class Foo{
private String a;
private String b;
public Foo(String a, String b){
this.a=a;
this.b=b;
}
//Getters go here
}
We have many many pojos like this that are used in various rest services across various parts of our tomcat platmform. The problem we found is that users can set a sql query as a value for Foo so when we create it and save it, they can do sql injection:
//example of possible injection
Foo foo = new Foo("select * from *;", "insert * into * as *");
//save new Foo to hibernate
session.saveOrUpdate(foo); //above queries will be executed on insert
The above is just a generic example of what we are seeing.
I have looked all over and so far all I have found is examples for sanitizing data on queries and not on insert values through hibernate. Is there a way through hibernate to have it sanitize the data of a pojo before inserting it into the db?
Simple Answer:
You need to esnure that you are NOT concatenating the HQL/SQL query strings with input data directly like below:
//Unsafe Hibernate query, Never do this
Query query = session.createQuery(" from Employee where empId='"+inputEmpId+"'");
Rather, you need to set the data using setParameter() methods provided by org.hibernate Query API (or for JDBC PraparedStatements use setString(""), etc..) like below:
//Safe Hibernate query
Query query = session.createQuery(" from Employee where empId=:empId");
query.setParameter("empId", inputEmpId);
For long answer, you can look at here.
Related
Currently I use the following code for a custom SQL statement in a Spring Data JDBC Repository interface:
default void upsert(LoanRecord r) {
upsert(r.getId(), r.getUcdpDocumentFileId(), r.getFreSellerNo(),
r.getFreDeliverySellerNo(), r.getLenderLoanNo(), r.getFreLpKey());
}
#Modifying
#Query("""
INSERT INTO loan (id, ucdp_document_file_id, lender_loan_no, fre_seller_no, fre_delivery_seller_no, fre_lp_key)
values (:id, :ucdpDocumentFileId, :lenderLoanNo, :freSellerNo, :freDeliverySellerNo, :freLpKey)
ON CONFLICT (ucdp_document_file_id) DO UPDATE SET
lender_loan_no = EXCLUDED.lender_loan_no,
fre_seller_no = EXCLUDED.fre_seller_no,
fre_delivery_seller_no = EXCLUDED.fre_delivery_seller_no,
fre_lp_key = EXCLUDED.fre_lp_key""")
void upsert(UUID id, String ucdpDocumentFileId, String freSellerNo,
String freDeliverySellerNo, String lenderLoanNo, String freLpKey);
The actual statement doesn't really matter, but as you can see, there's a wrapper method that unpacks the object so I can use simple parameters in the second method. Is there some way to refer to object properties in the query (like with MyBatis) so we can get rid of the wrapper method?
For reference, my spring-data-jdbc version is 2.4.2.
This is possible with the latest milestone release of Spring Data JDBC(3.0.0-RC1!
It is probably the main use case behind SpEL support.
With it you can now use constructs like this:
#Query("select u from User u where u.firstname = :#{#customer.firstname}")
List<User> findUsersByCustomersFirstname(#Param("customer") Customer customer);
Just as you can for a long time in Spring Data JPA.
I am trying to run some native SQL queries in my Spring application. I donĀ“t have an entity or JpaRepository class. I know it's strange, but this is a microservice just to collect two count queries and send it to Kafka.
Well trust me, all I need is these two integers from the queries. I run these code and always returns 0. I can see in the logs that Hikari is connecting to the database, so I don't know what to do. Searched a lot, but all answers involved the #Query solution, which does not work for me.
#Repository
#AllArgsConstructor
public class ReportRepository {
private final EntityManager em;
public int numberOfAccounts() {
String sql = "SELECT count(*) FROM account";
Query query = em.createNativeQuery(sql);
System.out.println(query.getFirstResult());
return query.getFirstResult();
}
public int numberOfSubscriptions() {
String sql = "SELECT count(*) FROM subscriptions";
Query query = em.createNativeQuery(sql);
System.out.println(query.getFirstResult());
return query.getFirstResult();
}
}
If you have EntityManager, and from what you are saying it can connect to DB, try this way:
public int numberOfSubscriptions() {
// >> "subscriptions" has to be the exact name of your table
// if does not work, consider trying SUBSCRIPTIONS or Subscriptions
String sql = "SELECT count(*) FROM subscriptions";
Query query = em.createNativeQuery(sql);
// getSingleResult() instead :)
return ((Number) query.getSingleResult()).intValue();
}
There is this (a bit old) JavaDoc for Query.getFirstResult() :
The position of the first result the query object was set to retrieve. Returns 0 if setFirstResult was not applied to the query object
So, I'd say that is not the right method for your case.
Happy Hacking :)
You should be using JDBC instead of an Entity Manager. Under the JPA uses JDBC but it requires defined entites to work. JDBC allows you to manage the connection and run the raw SQL queries.
Here's a link for how to do it in Spring:
https://spring.io/guides/gs/relational-data-access/#_store_and_retrieve_data
I'm using spring JPA with PostgreSQL database. I have an Entity as follow:
#Entity
#TypeDef(name="json_binary", typeClass = com.vladmihalcea.hibernate.type.json.JsonBinaryType.class)
public class LiveTokens {
#Id
#GeneratedValue()
private Long id;
private String username;
#Type(type="json_binary")
#Column(columnDefinition = "jsonb")
private Token token
}
and Token:
public class Token {
private Long expireTime;
private String accessToken;
}
For saving object to column with Hibernate, I use Hibernate Types project.
Now I want to get All LiveTokens that expired. I can't do it with Spring JPA. How do I query to posgresql jsonb column with Spring data JPA?
SQL JSON functions
If you want to call a SQL function that processes the JSON column, then you just need to make sure you register the function with Hibernate prior to using it.
JSON operators
For JSON operators, like the PostgreSQL ones (e.g., ->>), the only option you have is to use a native SQL query. JPQL or HQL don't support database-specific JSON operators.
Using EclipseLink and spring data jpa, if your data in db is something like: {"expireTime":102020230201, "accessToken":"SOMETHING" }, my first question is why to use long numbers for your dates instead of timestamps (ex '2019-09-14 12:05:00'). If you use timestamps there are also options to manage timezones (either from postgresql or from you source code).
Regarding your issue you may use the FUNC JPQL keyword of EclipseLink (Hibernate may have something similar) in order to run a database specific function. In the example below I use FUNC('jsonb_extract_path_text', lt.token, 'expireTime') to get the values of the json for token.expireTime.
PostgreSql method jsonb_extract_path_text returns text, thus you cannot do a less that condition, so I cast the output of the function using JPQL CAST keyword with (CAST -data- TO -type-).
#Repository
public interface MyRepository extends JpaRepository<LiveTokens, Integer> {
#Query(value = "SELECT lt FROM LiveTokens lt WHERE CAST(FUNC('jsonb_extract_path_text', lt.token, 'expireTime') AS LongType) < :expirirationThreshold")
List<LiveTokens> findByExpireTime(#Param("expirirationThreshold") Long expirirationThreshold);
}
Again, this is not tested.
This is how a native query using postgres JSON query operators would look like, incoorporating your example:
#Query(value="SELECT t.* FROM LiveTokens t WHERE CAST(t.token ->> 'expireTime' AS LONG) < now()", native=true)
Assuming your real tablename is LiveTokens, native queries do no longer use the JPA translations, and the tablename has to match the one in the DB. (You may also need to specify it's schema.)
Try it:
Service class:
Long currentTime = new Date().getTime();
Repository:
#Query("SELECT lt FROM LiveTokens lt WHERE lt.token.expireTime <= :currentTime")
List<LiveTokens> findExpiredLiveTokens(#Param("currentTime") long currentTime)
I'm switching from deprecated (unfortunately) Hibernate Criteria API to JPA Criteria API. We have a custom Order (from Hibernate) interface implementation to redefine SQL generated for it. The case is quite sophisticated as we need to use a giant SELECT with subqueries. We implemented toSqlString method of the interface to return this huge SQL and we need a way to migrate it to JPA Criteria API.
The question is: is there a way in JPA Criteria API to redefine the SQL generated? Or is there a weird way to use Hibernate Order with JPA Criteria API?
Thank you!
UPDATE Although #Tobias Liefke suggestion is quite interesting, my SQL varies too much to create a function class per SQL. I tried implementing a single function class and passing the SQL there as an argument but that didn't work (the rendered SQL was enclosed in single quotes thus it was sent to the database as parameter and not as part of the generated query)
You can't use SQL fragments in JPQL or criteria queries...
... except when ...
1. Calling a function
JPA and Hibernate allow to use functions in their expressions, for example:
... ORDER BY trim(entity.label) ASC
Resp.
query.orderBy(criteriaBuilder.asc(
criteriaBuilder.function("trim", String.class, root.get(ExampleEntity_.label))));
The problem is, that this is not really the call to the SQL function trim, but the call to a JPA function, which must be registered (Hibernate does this already for the most common SQL functions).
Fortunately you can define your own JPA functions in a DialectResolver:
public class MyDialectResolver implements DialectResolver {
public Dialect resolveDialect(final DialectResolutionInfo info) {
Dialect dialect = StandardDialectResolver.INSTANCE.resolve(info);
dialect.registerFunction("myOrderFunction", ...);
return dialect;
}
}
registerFunction takes two parameters, the first is the name of the function in JPA, the other is the mapping to SQL.
Don't forget to declare your dialect resolver in your persistence.xml:
<persistence-unit name="database">
<properties>
<property name="hibernate.dialect_resolvers"
value="my.package.MyDialectResolver" />
</properties>
</persistence-unit>
You could now create your own function in your SQL server which contains your huge SQL and register that as function:
dialect.registerFunction("myOrderFunction",
new StandardSQLFunction("myOrderFunctionInSQL", StringType.INSTANCE));
Or you could write your own mapping, which includes your huge SQL:
public class MyOrderFunction implements SQLFunction {
public String render((Type firstArgumentType, List arguments,
SessionFactoryImplementor factory) throws QueryException) {
return my_huge_SQL;
}
// ...
}
And register that one:
dialect.registerFunction("myOrderFunction", new MyOrderFunction());
Another advantage of this solution: you could define different SQLs depending on the actual database dialect.
2. Using a formula
You could use an additional attribute for your entity:
#Formula("my huge SQL")
private String orderAttribute;
You could now sort by this attribute:
... ORDER BY entity.orderAttribute ASC
Resp.
query.orderBy(criteriaBuilder.asc(root.get(ExampleEntity_.orderAttribute))));
I only recommend this solution, if you need the result of the huge SQL in your model anyway. Otherwise it will only pollute your entity model and add the SQL to every query of your entity (except you mark it with #Basic(fetch = FetchType.lazy) and use byte code instrumentation).
A similar solution would be to define a #Subselect entity with the huge SQL - with the same drawbacks.
I have a spring application that should connect to an existing database and just query an entity for existence based on some attributes.
I don't want to create a #Entity class for this. But I still want to use the spring managed EntityManager etc.
When using Spring, what is the best approach to just query a select for that entity? Using em.createNamedQuery(QUERY); with String QUERY = "SELECT count(*) from my_table where username =: username AND email := email)";?
Answers from #predrag-maric and #pL4Gu33 are both correct but if you use JPA in your project (for example, Hibernate) you might consider using #NamedNativeQuery annotation as well.
More about named native queries.
simple example of native query
#PersistenceContext
EntityManager em;
public String test(Integer id)
{
Query query = em.createNativeQuery("SELECT name FROM Accounts where id=?");
query.setParameter(1,id);
return query.getSingleResult();
}
You can use this method from entitymanager. http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#createNativeQuery%28java.lang.String%29
Use em.createNativeQuery(QUERY). Also, you'll have to use positional parameters (?1) instead of named parameters (:email), because only positional parameters are supported by JPA in native queries.