Java SQL Escape without using setString - java

Is there a built-in method to escape a string for SQL? I would use setString, but it happens I am using setString multiple times in the same combined SQL statement and it would be better performance (I think) if the escape happened only once instead of each time I say setString. If I had the escaped string in a variable, I could re-use it.
Is there no way to do this in Java?
Current method, multi-source search. In reality they are three entirely different where statements including joins, but for this example I will just show the same where for each table.
String q = '%' + request.getParameter("search") + '%';
PreparedStatement s = s("SELECT a,b,c FROM table1 where a = ? UNION select a,b,c from table2 where a = ? UNION select a,b,c FROM table3 where a = ?");
s.setString(1, q);
s.setString(2, q);
s.setString(3, q);
ResultSet r = s.executeQuery();
I know this is not a big deal, but I like to make things efficient and also there are situations where it is more readable to use " + quote(s) + " instead of ? and then somewhere down the line you find setString.

If you use setString for a parameter (e.g. PreparedStatement.setString), there may well be no actual escaping required - it's likely that the data will be passed separately from the SQL itself, in a way that doesn't require escaping.
Do you have any concrete indication that this really is a performance bottleneck? It seems very unlikely that within a database query, the expensive part is setting the parameters locally...

Short answer: I wouldn't bother. It's best to do escaping at the last popssible moment. When you try to escape a string early and keep it around, it becomes much more difficult to verify that all strings have been escaped exactly once. (Escaping a string twice is almost as bad as not escaping it at all!) I've seen plenty of programs that try to escape strings early and then run into trouble because they need to update the string and then the programmer forgets to re-do the escape, or they update the escaped version of the string, or they have four strings and they escape three of them, etc. (I was just working on a bug where a programmer did HTML escapes on a string early, then decided he had to truncate the string to fit on a form, and ended up trying to output a string that ended with "&am". That is, he truncated his escape sequence so it was no longer valid.)
The CPU time to escape a string should be trivial. Unless you have a very large number of records or very big strings that are re-used, I doubt the savings would be worth worrying about. You'd probably be better off spending your time optimizing queries: saving a read of one record would probably be worth far more than eliminating 1000 trips through the string escape logic.
Longer answer: There's no built-in function. You could write one easily enough: Most flavors of SQL just need you to double any single quotes. You may need to also double backslashes or one or two other special characters. The fact that this can be different between SQL engines is one of the big arguments for using PreparedStatements and letting JDBC worry about it. (Personally I think there should be a JDbC function to do escaping that could then know any requirements specific to the DB engine. But there isn't so that's how it is.)
In any case, it's not clear how it would work with a PreparedStatement. There'd have to be some way to tell the PreparedStatement not to escape this string because it's already been escaped. And who really knows what's happening under the table in the conversation between JDBC and the DB engine: Maybe it never really escapes it at all, but passes it separately from the query. I suppose there could be an extra parameter on the setString that says "this string was pre-escaped", but that would add complexity and potential errors for very little gain.

Do not use org.apache.commons.lang.StringEscapeUtils.escapeSql(yourUnscapedSQL);
It does not escape characters like \

You can use StringEscapeUtils from Apache commons:
org.apache.commons.lang.StringEscapeUtils.escapeSql(yourUnscapedSQL);

Related

PreparedStatement IN clause Regexp alternative?

I came across the same issue as the author of this question (PreparedStatement IN clause alternatives?), and wondered if using mysql's REGEXP would be an elegant way of getting the same functionality of IN while using only one PreparedStatement for varying number of values to match? Some example SQL here to show what I am talking about:
SELECT first_name, last_name
FROM people
WHERE first_name REGEXP ?
Multiple values could be supplied using a string like "Robert|Janice|Michael". I did not see REGEXP mentioned anywhere in that post.
Technically, yes, it is an alternative.
Note, however, that using a regex for matching is less efficient that the in operator ; it incurs more work for the database, that needs to initialize the regex engine, and run it against each and every value (it cannot take advantage of an index).You might not notice it on small volumes, but as your data grows larger this might become an issue. So I would not recommend that as a general solution: instead, just write a few more code lines in your application to properly use the in operator, and use regexes only where they are truly needed.
Aside: if you want to match the entire string, as in does, you need to surround the list of values with ^ and $, so the equivalent for:
first_name in ('Robert', 'Janice', 'Michael')
Would be:
first name regexp '^(Robert|Janice|Michael)$'
Another approach:
FIND_IN_SET(name, 'Robert,Janice,Michael')
Yes, that could be substituted in. But it must be a commalist of the desired values. This also works for FIND_IN_SET(foo, '1,123,45'). Note that 12 will not match.

Preventing Redshift SQL Injection

I have the unfortunate situation where I have to build up a SQL string by concatenating strings - the classic SQL injection scenario. I can't use prepared statements.
If I escape the ' character am I safe? Or are there other attack vectors?
I'm using MyBatis and it's ${} notation (vs #{} that generates prepared statements). I have no choice with this - it has to be ${}. I can't use prepared statements.
EDIT:
To add a little more clarity; it's an ASW Redshift UNLOAD command. The first parameter for UNLOAD is a SQL string.
(Given that you cannot do it the correct way because of restrictions in Redshift):
On PostgreSQL with standard_conforming_strings set to on all you need to do is double quotes, making ' into ''. That's it.
Backslashes aren't significant unless standard_conforming_strings is off or you use an E'' string. If either of those things are true then you have to do backslash escaping instead.
As Redshift is based on a fork of an ancient PostgreSQL version I don't know for sure how this applies to it. Reading the documentation on its lexical structure and syntax would be wise, to verify that it is consistent with how PostgreSQL works.
PreparedStatement (Wikipedia) really is the way to go. In one fell swoop you eliminate a big pile of work and risk regarding SQL Injection hackers.
If you absolutely can’t/won’t use PreparedStatement, then you need to read about various strategies. You'll have to write a bunch of checks to examine and modify your inputs and SQL. No silver bullet. (Well, actually, PreparedStatement is your silver bullet. But no other silver bullet.)
Google for items like "sanitize sql input". You will find resources such as:
Bobby-Tables.com (which tells you to use PreparedStatement).
Mitigation section of Wikipedia page on SQL Injection.
Article, Prevent Web Attacks Using Input Sanitization.
Article, How to prevent SQL injection attacks?, that explains with examples how sanitizing input is not enough, and recommends using … yes, you guessed it: PreparedStatement.

Is String.split() function safe?

One day I was talking with a friend about one of his server applications for a little flash game.
The server communicates with a mysql database. And I found this request:
"UPDATE phpbb_users SET patojdur = '" + this.score + "' WHERE user_id = '" + this.user_id + "'"
As this.score is data entered by the user, I asked him if it wasn't unsafe to put that directly in the SQL request, and take the risk of an SQL injection.
But he answered me: "No, because this.score is an user_request.split("'")[1], the split is protecting me and you can't put a ' to inject."
My question isn't if he made the right choice by doing that, because I know he won't change his mind, but What he said made me curious about a thing: is split really safe? Does it really prevent the splitted character to pass whatever you do? Or even if it's risky, put a var.split("'") finally prevent you from ' injection?
Edit: I've read the following question but mine is specific to the Split method, and doesn't apply only on SQL database, in other word my question is:
Does var.split('c') really prevent c to be in the final string?
Still dangerous. Of course it depends on the SQL variant. Backslash is by the standard an escape. Easy would be \x27 (if that works) for apostrophe; but already havoc is possible if the injected string ends with a backslash.
'ʼ;DROP TABLE myTable--'
there are cases where Unicode conversion might slip through a single quote, since you are only explicitly replacing one representation of the sincle quote charater with an empty string (thats what split does ..)
see: https://siderite.dev/blog/why-doubling-single-quotes-is-not.html

JPQL LIKE syntax with Strings

EDIT:
I changed the hard coded query to be:
query.setParameter("desc", "%unplug //your// server... enjoy the freedom%" ESCAPE '//')
and now I am getting an com.sun.jdi.InvocationException occurred invoking method.
There's no stacktrace produced either.
I have a description column in my PostgreSQL database and I am trying to query it with a 'LIKE' clause, however I am unable to get any results. Here's an example:
Query query = em.createQuery("from MyClass c WHERE c.description LIKE :desc");
query.setParameter("desc", "%unplug /your/ server... enjoy the freedom%");
In the database I have many descriptions containing a substring of the above text. I've done a lot of research and looked into escaping special chars etc, but nothing has worked.
I am missing something, I just cannot figure out what that is.
Most likely the slash / is messing up the parsing in PG. Turn the parameter into a quoted literal. Unless you are certain that no special characters go into string arguments (i.e. you control the strings), this is always a good idea to avoid SQL injection.
query.setParameter("desc", "quote_literal('%unplug /your/ server... enjoy the freedom%')");

How to escape special characters used in SQL query?

Is there a Java library for escaping special characters from a string that is going to be inserted into an SQL query.
I keep writing code to escape various things, but I keep finding some new issue trips me up. So a library that takes care of all or most of the possibilities would be very handy.
EDIT: I am using MySQL (if that makes any difference).
Well... jdbc. Pass the strings as parameters, and don't append them to the query string
A little bit more research points me to this:
http://devwar.blogspot.com/2010/06/how-to-escape-special-characters-in.html
Which suggests to use apache.commons.lang.StringEscapeUtils, I will try this out
I know this is a long time thread, but using the commonslang library there is a method called escapeSql(String). Also using prepared statement automatically escape the offending SQL character.

Categories