I'm writing this question because I didn't find any useful article about how to prevent SQL Injection in Spring Data JPA. All the tutorials are showing how to use these queries but they don't mentioned anything about these possible attacks.
I'm having the following query:
#Repository
public interface UserRepository extends CrudRepository<User, Integer> {
#Query(nativeQuery = true, value = "SELECT * FROM users WHERE email LIKE %:emailAddress%")
public ResponseList<User> getUsers(#Param("emailAddress") String emailAddress);
}
The rest controller to deliver the request:
#RequestMapping(value = "/get-users", method = RequestMethod.POST)
public Response<StringResponse> getUsers(WebRequest request) {
return userService.getUsers(request.getParameter("email"));
}
Are JPQL or native query parameters escaped before executing them?
This is the query with SQL injection executed in my MySQL console which drops the users table:
SELECT * FROM users WHERE email LIKE '%'; DROP TABLE users; -- %';
I have tried to execute the SQL attack by sending a POST request to the server:
http://localhost:8080/get-users
POST: key/value: "email" : "'; DROP TABLE users; --"
I have enabled Hibernate's sql logging and this is what the above request produced:
[http-nio-8080-exec-8] DEBUG org.hibernate.SQL - SELECT * FROM users WHERE email LIKE ?
Hibernate: SELECT * FROM users WHERE email LIKE ?
[http-nio-8080-exec-8] DEBUG org.hibernate.loader.Loader - bindNamedParameters() %'; DROP TABLE users; -- % -> emailAddress [1]
[http-nio-8080-exec-8] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [%'; DROP TABLE users; -- %]
The table wasn't dropped (which is good) but why the parameter isn't escaped?
What if I don't annotate the #Param("emailAddress") and I use indexed parameters?:
#Query(nativeQuery = true, value = "SELECT * FROM users WHERE email LIKE ?1")
public ResponseList<User> getUsers(String email);
Spring Data JPA uses bind parameters for all its features. There is very little String concatenation going on and that only with stuff that doesn't come from end users.
Therefore Spring Data JPA is secure against SQL injection.
What if I don't annotate the #Param("emailAddress") and I use indexed parameters?
This is independent of you using indexed or named parameters.
why the parameter isn't escaped?
Since the bind parameter values are not part of the SQL statement that gets parsed and converted into an execution plan in the database there is no need to escape the parameters. Indeed it would be a mistake to do so.
Related
I'm querying a PostgreSQL table with a very simple query using Spring Data JPA:
#Query(value = "SELECT id FROM location l", nativeQuery = true)
This works fine. However, if I omit the table alias and use:
#Query(value = "SELECT id FROM location", nativeQuery = true)
I get an error saying No Dialect mapping for JDBC type: 1111. ID is a UUID in both PostgreSQL and the DTO.
Why does this happen? As far as I can tell, that's a perfectly valid query, and I'm not even using the table alias in the working version. Adding #Type and #TypeMappings made no difference. I checked the data I'm querying, and all the IDs are valid.
I'm happy to use an alias now that I've figured this out, but I'm curious about what's happening behind the scenes that causes this to fail.
Spring Data JPA tries to parse your query in order to derive a count query or add/modify and ORDER BY clause.
This to some extend relies on having an alias for the main table/entity of the query.
It should give you a proper error that it can't find an alias instead of the weird error you are seeing.
I am using Teiid vdb model where i need to extract query constraints inside the ddl and use it in a stored procedure to fetch results of my choice. For example, if I run following query :
select * from Student where student_name = 'st123', i want to pass st123 to my procedure and return the results based on some processing.
How can i extract this constraint inside the ddl instead of teiid doing the filtering for me and returning the matching row. Is there a way around developing the connector and handling this in vdb instead?
See http://teiid.github.io/teiid-documents/master/content/reference/r_procedural-relational-command.html
If you have the procedure:
create virtual procedure student (in student_name string) returns table (<some cols>) as
begin
if (student_name like '...')
...
end
then you can all it as if it were a table:
select * from student where student_name = 'st123'
I wrote a #Query in JPA to fetch the details by json property which is resulting error.
#Query("SELECT t FROM Tcl order by t.equipment->>'eqpm_n' ASC")
public List<Tcl> getEquipmentList();
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token
SELECT t FROM com.target.mpe.models.Tcl order by t.equipment->>'eqpm_n' ASC
And same query is working well in Postgres console.
How can i make it work in SpringBoot JPA?
Do i need to try Native Query?
JPQL does not support this PostgreSQL syntax. You have to use a nativeQuery:
#Query(value = "SELECT * FROM Tcl t order by t.equipment->>'eqpm_n' ASC", nativeQuery = true)
public List<Tcl> getEquipmentList();
I am using EF Core 1.1 and have a query like
var list=from l in context.Users
where l.SomeProp==someVal
select l;
I have a UDF which returns a table of Id's and I basically want to generate the following query:
select * from Users where SomeProp=#someVal and SomeId in (select id from fn_myfunc(#id))
Is this possible to do?
I think you are limited to running a raw SQL query against the database to be able to use a table valued function. For example:
var query = #"SELECT * FROM Users WHERE SomeProp = {0}
AND SomeId IN (SELECT id FROM fn_myfunc({1})";
var users = context.Users
.FromSql(query, someProp, someId)
.ToList();
There are some limitations with this method. From the docs:
SQL queries can only be used to return entity types that are part of your model. There is an enhancement on our backlog to enable returning ad-hoc types from raw SQL queries.
The SQL query must return data for all properties of the entity type.
The column names in the result set must match the column names that properties are mapped to. Note this is different from EF6.x where property/column mapping was ignored for raw SQL queries and result set column names had to match the property names.
The SQL query cannot contain related data. However, in many cases you can compose on top of the query using the Include operator to return related data.
You can return related data (i.e. Include) like this:
var users = context.Users
.FromSql(query, someProp, someId)
.Include(u => u.OtherThings)
.ToList();
If you need to do anything more complex, then you would need to either drop down to using raw data access (like ADO.Net) or another ORM. I know people who use EF Core for the bulk of the work and then occasionally drop into Dapper for performance or raw SQL that doesn't suit EF.
I am using
length(ze.string)>2 in openJpa query. but i am getting
SQLCODE=-440, SQLSTATE=42884, SQLERRMC=CHAR_LENGTH;FUNCTION, DRIVER=3.53.95 {prepstmnt 1776269692 SELECT t0.f1, t0.f2, t0.f3, t0.f4, t0.f5, t0.f6, t0.f7, t0.f8, t0.f9, t0.f10, t0.f11, t0.f12, t0.f13, t0.f14, t0.f15, t0.f16, t0.f17 FROM table t0 WHERE (t0.f1 = ? AND CHAR_LENGTH(?) > ? AND .....
In plain query when i do length operation i am getting record but using jpa its not working. I looked Here used size it doesn't work. and the field is varchar and db2. trying from past 1 hour.
DB2 requires use of the SQL function LENGTH, yet OpenJPA seems to be incorrectly converting your JPQL to use SQL function CHAR_LENGTH (hence the error message - not that DB2 gives out clear messages saying what is wrong, who knows what SQLCODE=-440 is without having to search!!).
Raise a bug on your JPA provider.
See https://www.ibm.com/support/knowledgecenter/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000818.html
You would need to give more details about your entity, persistence.xml, and query to get to the bottom or this. However, I do not see how OpenJPA would use CHAR_LENGTH instead of LENGTH for DB2. Let me explain. If you look at DBDictionary here:
https://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?view=markup
You can see it defines something called "stringLengthFunction" as follows:
public String stringLengthFunction = "CHAR_LENGTH({0})";
This is the string length function which should be used for each individual dictionary (i.e. Database config). However, for DB2, the AbstractDB2Dictionary, see here:
https://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/AbstractDB2Dictionary.java?view=markup
overrides this as follows:
stringLengthFunction = "LENGTH({0})";
Given this, for DB2, LENGTH should be used. I took the following simple query:
"select me.id from MyEntity me where length(me.name)>2"
And executed it on OpenJPA using DB2, and I got this:
SELECT t0.ID FROM MYENTITY t0 WHERE (CAST(LENGTH(t0.ID) AS BIGINT) > CAST(? AS BIGINT)) [params=(long) 2]
Thanks,
Heath Thomann