Is a select query made by a java driver accessor vulnerable to injection?
Some like
#Query("SELECT * FROM table WHERE id = :id")
Result<Entity> byId(#Param("id") String id);
No, This syntax does not perform string replacement. It actually binds the value into a parameter slot. This means a malicious request would simply be treated as the id within a bound statement.
Related
This question already has answers here:
How to replace table name with parameter value while using Spring Data JPA nativeQuery
(5 answers)
Closed 3 years ago.
I am using Hibernate and Spring data JPA to build a web project.
In Eclipse-Link we can use native query like
String tableName = "sometablename";
String query = "SELECT * FROM " +tableName +"WHERE id > 10";
In Hibernate I am using #Query annotation
#Query(value = "SELECT COUNT(r.id) as resultsCount FROM #{#resultTable} r WHERE r.customerId= :customerId AND r.is_deleted=0 AND r.is_ignored=0 ", nativeQuery = true)
Integer getResultsCount(#Param("customerId") int customerId,
#Param("resultTable") String resultTable);
I tried #{#resultTable} but this is getting replaced as a string with quote and i am getting an exception that
You have a error in your SQL Syntax
I want to use table name dynamically from params. Is it possible? And if yes, Please tell me how?
It's not possible, #org.springframework.data.jpa.repository.Query takes only jpql, you cannot pass the name of the table since it's not recognized as any entity.
It states the javadoc of Query:
/**
* Defines the JPA query to be executed when the annotated method is called.
*/
String value() default "";
The best solution would be not to pass tablename as string, but resolve it using for example inheritance (link) or rebuild your datamodel somehow. As a quick and dirty solution I would suggest creating a custom repository and using EntityManager.createNativeQuery and pass the sql there. But remember to validate the query you're composing (validate user input, use enums for table names), because it can lead to sql injection.
Your own observations actually answer your question:
I tried #{#resultTable} but this is getting replaced as a string with quote and i am getting an exception
The placeholders which are used inside the #Query query string are intended to filled with literal values. Hence, the table name ended up appearing as literal string, inside single quotes. This means that behind the scenes #Query and Spring are probably using a JDBC prepared statement. It is not possible to bind the name of the table. Allowing this would be a major security hole.
The only possible workaround here would be to concatenate your query string together, and then trying to use that string with #Query. But note that this would not be a safe option.
What you are doing is wrong, you are mixing business logic into DAO layer, i suggest you create two DAO's, each one with its own table and query, then into the business/service layer call the desired one.
#Query(value = "SELECT * FROM H4 WHERE 1")
List getResult();
Instead of the query "SELECT * FROM H4 WHERE 1" I want to put a String variable containing query generated elsewhere.
#Query, like any other annotation, uses a compile time constant to define attributes. You can't define it's value in runtime unless you plan to hack the Spring Data JPA framework.
You should use either Specifications with criteria, Query by example or JPQL to define and execute your dynamic query.
Create constants and set the value as below.
#Query(value = ApplicationConstantClass.QUERY_STRING_CONSTANT)
List getResult();
or Use EntityManager or SessionFactory of hibernate to execute dynamically generated query.
Can I pass an attribute as parameter in a method from my Repository?
Example:
#Query("select a from Account a where :attr = :value")
public Page<Account> searchByFilter(#Param("attr")
String attribute,#Param("value")String value,Pageable pageable);
Call example :
searchByFilter("status","Active",....);
Thanks
No, it is not possible.
Basically your query can be compiled and successfully executed, however it will be translated not the way you like.
At the end you'll have sql like this
select a.* from account a where 'status' = 'Active';
Note: status is translated to string value, not the column name.
I need to set a table name dynamically so that I use query.setText(tname,abc)
e.g: select a.name from :tname where a.id = '2'
I used setText() because when I use setString() it says "tname is a invalid parameter" because I assume that Hibernate adds '' when setting string parameters.
But even setText() does not help and gives the same exception.
How can I set the table name dynamically?
Reply to PSR:
So you mean replace table name as a java string replacement. But then we can not take support of sql injections prevention etc from hibernate right? Also How we bind parameters in hibernate in a situation where like statement,
Eg: name like "%:name%"
This also gives me Illegal argument exception: Parameter does not exist as a named parameter when i try to bind it using query.setString(name,"def");
Hibernate will not do this for you, because it works with PreparedStatements, and you can't prepare a statement where the table being queried isn't known yet.
I don't see why you would be exposing table names to end users, so preventing SQL injection doing a regular string substitution should be easy. You use some sort of business logic to determine the correct table from a list that only you know. The table name isn't coming from user input at all.
Depending on your choice of RDBMS, you may find a discriminator column, or table inheritance with partitioning to be a better way of handling a situation where identical queries are made against different tables.
It is not possible to set table name dynamically.You can set dynamically column names.it is not possible to set table name
try like this
select a.name from '+table name+'where a.id = '2'
In my opinion, There are 2 ways to resolve this issue:
1- If you are using Spring and Hibernate together, you could use SpEL and it would be like #{#entityName} as it is described here
#Entity
public class User {
#Id
#GeneratedValue
Long id;
String lastname;
}
public interface UserRepository extends JpaRepository<User,Long> {
#Query("select u from #{#entityName} u where u.lastname = ?1")
List<User> findByLastname(String lastname);
}
2-You could use CriteriaBuilder like
CriteriaQuery<YourEntity> cr = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cr.from(YourEntity.class);
cr.select(root);
I copied the source codes from the provided links and they are described there much better
I have an object in which a member is an enum, and I want to write a query returning all elements for which that member is in a list of values. So, I've written the following JQP query
#NamedQuery(name = MyBean.FIND_BY_STATUSES, query = "select t from "+MyBean.TABLE+" t where t.status member of :statuses"),
class MyBean {
#Enumerated(EnumType.STRING)
private MyEnum status;
}
That I try to get using the following EJB code
Query findByStatuses = getEntityManager().createNamedQuery(MyBean.FIND_BY_STATUSES);
findByStatuses.setParameter("statuses", Arrays.asList(statuses));
return findByStatuses.getResultList();
Unfortunatly, Glassfish endlessly tells me I'm wrong (which I am, obviously).
But what do I have to fix ? and how ?
First your query references to table name, so it tries to be kind of SQL, but maybe database does not have "MEMBER OF"-operator which would be suitable for this kind of use, so let's try with JPQL. There we cannot use MEMBER OF, because it takes path expression as an argument, and that is not what we provide in setParameter. Instead we use IN:
#NamedQuery(name = MyBean.FIND_BY_STATUSES, query = "select t from MyBean t where t.status in(:statuses)")
No need to modify part where you run query.