What are the risks when using StringBuilder and JPA native queries? - java

I need to create a native sql query and I plan to use StringBuilder for creating it.
StringBuilder sqlQuery = new StringBuilder();
sqlQuery.append("SELECT ");
sqlQuery.append("b.\"id\", ");
sqlQuery.append("b.\"title\", ");
sqlQuery.append("a.\"author_name\" as author ");
sqlQuery.append("FROM ");
sqlQuery.append(":dbName b ");
sqlQuery.append("JOIN ");
sqlQuery.append(":dbName2 a");
sqlQuery.append(" ON a.\"id\" = b.\"author\"");
ObjectQuery query = objectManager.createQuery(sqlQuery.toString());
query.setParameter("dbName", "Book");
query.setParameter("dbName2", "Author");
Is it safe to use the append of StringBuilder? Will this not result in SQL injection from an attacker? Will attacker be able to append a part of query that will drop the entire database? Any suggestions?
I know this query is simple and can be written in a single string but I have bigger queries with if statements and loops that append more lines depending on parameters
I know the named query is safer but in my case I don't know what the query will be like until the last moment.

If you set the parameter values with Query.setParameter() as in your sample code :
query.setParameter("dbName", "Book");
query.setParameter("dbName2", "Author");
you should not have SQL injection issues.
SQL injection happens as you concatenate yourself the user data in the created query.
But you are not in this case as the query created by the StringBuilder object is mastered totally by you and doesn't take any values from an external client.
Is it safe to use the append of StringBuilder?
While the StringBuilder variable (beware : StringBuilder is not thread-safe) is created and manipulated in a method scope, it is not more or less safe than another solution.
It doesn't have any relationship with safety but as #HRgiger suggested, a good alternative would be using Criteria API.
It makes sense as you explain that the construction of your queries is very dynamic :
I have much bigger query with if statements that decides what to
append
It would produce a much more maintainable and readable code.

In this case, there is no risk to get SQL injections attacks, simply because of using setParameter, but you have to note that StringBuilder is not thread safe type.

Related

Runtime SQL Query Builder

My question is similar to
Is there any good dynamic SQL builder library in Java?
However one important point taken from above thread:
Querydsl and jOOQ seem to be the most popular and mature choices however there's one thing to be aware of: Both rely on the concept of code generation, where meta classes are generated for database tables and fields. This facilitates a nice, clean DSL but it faces a problem when trying to create queries for databases that are only known at runtime.
Is there any way to create the queries at runtime besides just using plain JDBC + String concatenation?
What I'm looking for is a web application that can be used to build forms to query existing databases. Now if something like that already exists links to such a product would be welcome too.
While source code generation for database meta data certainly adds much value to using jOOQ, it is not a prerequisite. Many jOOQ users use jOOQ for the same use-case that you envision. This is also reflected in the jOOQ tutorials, which list using jOOQ without code generation as a perfectly valid use-case. For example:
String sql = create.select(
fieldByName("BOOK","TITLE"),
fieldByName("AUTHOR","FIRST_NAME"),
fieldByName("AUTHOR","LAST_NAME"))
.from(tableByName("BOOK"))
.join(tableByName("AUTHOR"))
.on(fieldByName("BOOK", "AUTHOR_ID").eq(
fieldByName("AUTHOR", "ID")))
.where(fieldByName("BOOK", "PUBLISHED_IN").eq(1948))
.getSQL();
In a similar fashion, bind values can be extracted from any Query using Query.getBindValues().
This approach will still beat plain JDBC + String concatenation for dynamic SQL statements, as you do not need to worry about:
Syntax correctness
Cross-database compatibility
SQL Injection
Bind variable indexing
(Disclaimer: I work for the vendor of jOOQ)
SQLBuilder http://openhms.sourceforge.net/sqlbuilder/ is very useful for me.
Some simple examples:
String query1 = new InsertQuery("table1")
.addCustomColumn("s01", "12")
.addCustomColumn("stolbez", 19)
.addCustomColumn("FIRSTNAME", "Alexander")
.addCustomColumn("LASTNAME", "Ivanov")
.toString();
String query2 = new UpdateQuery("table2")
.addCustomSetClause("id", 1)
.addCustomSetClause("FIRSTNAME", "Alexander")
.addCustomSetClause("LASTNAME", "Ivanov")
.toString();
Results:
INSERT INTO table1 (s01,stolbez,FIRSTNAME,LASTNAME) VALUES ('12',19,'Alexander','Ivanov')
UPDATE table2 SET id = 1,FIRSTNAME = 'Alexander',LASTNAME = 'Ivanov'
I have a custom solution for dynamically generating such SQL queries with just 2-3 classes for similar requirement. It is a simple approch.
This can be referred at Creating Dynamic SQL queries in Java
For simpler use cases like a dynamic filter condition based on the inputs selected from UI, one can use the below simpler approach by directly modifying the query in below style:
select t1.id, t1.col1, t1.col2,
from table1 t1
where (:col1Value is null or t1.col1 = :col1Value)
and (:col2Value is null or t1.col2 = :col2Value);
Here values for col1 or col2 can be null but the query will work fine.

Block SQL Injection entry in a jtextfield using regex

Is any other way to block sql injection in a textfield using regex?
This is what I use to block some special characters:
private static final String PATTERN =
"([^A-Za-z0-9.,'\"% _-]+)";
Just as I've said, SOME. I want to block ALL if possible and I only want to use regex. Please help. :)
The right way would be to use a PreparedStatement to execute your query. It will take care of possible injections.
You can find the docs here and the tutorial here
As for an example, after you set up a database connection, use something like this:
// add exception handling where necessary
PreparedStatement insertStmt = null;
String query = "INSERT INTO sometable"
+ "(name, value) VALUES"
+ "(?,?)"; //here the question marks stand for parameters
insertStmt = dbConnection.prepareStatement(query);
insertStmt.setString(1, "somename"); //set the first param
insertStmt.setInt(2, value); // set the second param
insertStmt.executeUpdate();
You will never be certain that you blocked all available SQL statements by using a regular expression. This is a very unstable and error prone approach. I would suggest using either a prepared statement or even better an ORM framework. You could check this tutorial regarding hibernate ORM and this tutorial for prepared statements.
These two mechanisms will protect you by definition from SQL injection as they are not based in using user input in their executed SQL, unless you specifically tell them so. Even then, they implement SQL injection protection which are far better than using a regular expression.
Hope I helped!

working with variable number of parameters in a preparedStatement

I'm creating a search form for my application.
In it user selects fields that should be used in filtering data.
the number fields is variable so I don't know how many ? should be in where clause of SQL query.
How can I use preparedStatement with variable number of conditions in where clause?
Thanks
if you want to add variable number of conditions in where clause use
StringBuilder (StringBuffer if its multithreaded env.) and and run time depending upon your conditions concate/append to your string.
like
StringBuilder query = new StringBuilder("Select id, name from Student ");
if(args >0)
{
query.append(" where "); //and add more args.
and later create prepared statement with this query by converting it to string
PrepareStatement(query.toString());
PrepardStatements don't support variable numbers of conditions. What some frameworks do, is they cache each PreparedStatement in a Map where the key is the query.
So each time you want to run a query, you need to build the string to create the PreparedStatement, check if you have it in the map (and reuse it) or create a new one, and add it to the map.

Doing Java String replacement efficiently

We have the following code :
String templateQuery = "select * from my_table where col1=$1 or col2 like '%$2.$1'";
String tmp = templateQuery;
for(int i=1;i<=maxCols;i++) {
tmp = tmp.replaceAll("\\$"+i, data[i-1]);
}
This code works fine as maxCols never exceeds 10. But my colleague disagree with me stating that this code consumes too much memory. Can you help us ?
EDIT:
I have change the initial templateQuery with a much realistic one. Secondly, templateQuery can potentially be a big string.
EDIT 2:
Thanks for those who have pointed out the SQLInjection problem.
Don't do this.
Not for performance reasons (which will be miniscule compared with the cost of the database query), but to avoid SQL injection attacks. What happens if data[0] is actually the string
' OR 'x' = 'x
?
Then you'll end up with a SQL statement of:
SELECT * FROM my_table WHERE col1='' OR 'x' = 'x'
which I think we can agree isn't what you wanted.
Use a parameterized SQL statement instead (PreparedStatement) and get the database driver to send the parameter values separately.
EDIT: In other comments, the OP has specified that the template string can be quite long, and some parameters may actually involve multiple initial values combined together. I still say that the cost of replacement is likely to be insignificant in the grand scheme of things, and I still say that PreparedStatement is the way to go. You should perform whatever combining operations you need to on the input before setting them as the values for the PreparedStatement - so the template may need the SQL with SQL placeholders, and then "subtemplates" to work out how to get from your input to the parameters for the PreparedStatement. Whatever you do, putting the values directly into the SQL is the wrong approach.
Why aren't you just using a PreparedStatement with replacement parameters?
String templateQuery = "SELECT * FROM my_table WHERE col1 = ?";
PreparedStatement ps = con.prepareStatement(templateQuery);
for (int i = 0; i < data.length; i++) {
ps.setString(i + 1, data[i]);
}
ResultSet rs = ps.executeQuery();
You're otherwise vulnerable to SQL injection if you use string replacement like you have.
He is correct, because you create maxCols tmp Strings.
I realized that it is for Sql commands, if is it, why you do not use PreparedStatement (http://download.oracle.com/javase/1.4.2/docs/api/java/sql/PreparedStatement.html) for this task?
Also, for formatting strings, rather than use substitute, use Formatter, it is much more elegant: http://download.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html
Whether this consumes too much memory is open to debate (what's "too much"?)
Nonetheless, for this kind of stuff you should use PreparedStatement. It allows you to do pretty much exactly what you're trying to achieve, but in a much cleaner fashion.
Your colleague is right in that every string replacement creates a new copy of the string. (However, the cost of these is probably negligible with less than 10 parameters.) Moreover, for every execution of this query the SQL engine needs to parse it afresh, which consumes far more additional resources each time.
The potential bigger problem though is that the code is suscept to SQL injection. If the input data is coming from an external source, a hacker can pass in a parameter such as "col1; drop table my_table;", effectively deleting your whole table.
All of these can be solved by using a PreparedStatement instead.

Faster way to use data from a String array?

Currently, I have a table being read from like so:
ps = con.prepareStatement("SELECT * FROM testrow;");
rs = ps.executeQuery();
rs.next();
String[] skills = rs.getString("skills").split(";");
String[] skillInfo;
for (int i = 0; i < skills.length; i++) {
skillInfo = skills[i].split(",");
newid.add(Integer.parseInt(skillInfo[0]));
newspamt.add(Byte.parseByte(skillInfo[1]));
mastery.add(Byte.parseByte(skillInfo[2]));
}
rs.close();
ps.close();
The information is saved to the database by using StringBuilder to form a string of all the numbers that need to be stored, which would be in the format of number1,number2,number3;
I had written a test project to see if that method would be faster than using MySQL's batch method, and it beat MySQL by roughly 3 seconds. The only problem I'm facing now is when I go to read the information, MySQL completes the job in a few milliseconds, where as calling the information using String[] to split the data by the ";" character, and then also using String[] to split information within a loop by the "," character, takes about 3 to 5 seconds.
Is there anyway I can reduce the amount of time it takes to load the data using the String[], or possibly another method?
Do not store serialized arrays in database fields. Use 3NF?
Do you read the information more often than you write it ? If so (most likely) then optimising the write seems to be emphasising the wrong end of the operation. Why not store the info in separate columns and thus avoid splitting (i.e. normalise your data)?
If you can't do that, can you load the data in one thread, and hand off to another thread for splitting/storing the info. i.e. you read the data in one thread, and for each line, pass it through (say) a BlockingQueue to another thread that splits/stores.
in the format of number1,number2,number3
consider normalising the table, giving one number per row.
String.split uses a regular expression for its algorithm. I'm not how it's implemented, but the chance is that is quite cpu heavy. Try implementing your own split method, using a char value instead of a regular expression.
Drop the index while inserting, that'll make it faster.
Of course this is only an option for a batch load, not for 500-per-second transactions.
The most obvious alternative method is to have a separate skills table with rows and columns instead of a single field of concatenated values. I know it looks like a big change if you've already got data to migrate but it's worth the effort for so many reasons.
I recommend that instead of using the split method, you use a precompiled regular expression, especially in the loop.

Categories