2

My application has a lot of JDBC queries written using Statement and is thus vulnerable to SQL injection. Unfortunately this application was developed around 10 years back and probably developers didn't know about Prepared statement.

I know that the best way to solve this problem is to use PreparedStatement but it is very tedious to convert it throughout the application. Also, writing any sort of Patten matching for SQL injection could be very tricky as keywords like Select, insert, union, etc are all English words which could also appear in a text field keyed in by the user.

Is there a smarter way to avoid SQL injection without using Prepared statement. If this is a duplicate question, please give me the link to question which has a good answer. Thanks for the help.

Arunabh
  • 167
  • 1
  • 7
  • 18
  • 1
    Possible duplicate of [How to escape string for SQL query (without prepared statement)](https://stackoverflow.com/questions/11448534/how-to-escape-string-for-sql-query-without-prepared-statement) – Bill Karwin Nov 13 '17 at 20:59
  • I think it would be more tedious to fix it without using prepared statements than with. Do yourself a favour and use the more solid option of using prepared statements. – Mark Rotteveel Nov 15 '17 at 14:25

1 Answers1

7

Hah. Unfortunately, it almost always depends on the monetary and managerial decision for what's appropriate, but "it is very tedious" is not generally considered a valid engineering concern -- it is but an excuse to appropriately refactor the code.

Meanwhile, the question requests non-PreparedStatement methods: in short, if you cannot offload the work to a library (e.g. prepared statements), then the only other method is to do it yourself for each "injected" item. Either way, the work of checking the input must be performed. The sole question is where it is done, and what programmer's expertise made the input validation code.

For example, consider a simple SELECT statement:

    sql = "SELECT * FROM mytable WHERE id = " + untrustedVar;

For completeness, we might assume the injection example where untrustedVar is a string like 1 OR 1 or 1; DROP TABLE mytable; Obviously this would result in unwanted behavior, vis-a-vis all rows returned to the caller, or a now missing database table:

SELECT * FROM mytable WHERE id = 1;
DROP mytable;

Obligatory XKCD reference

In this case, you could let the language semantics at least ensure that unstrustedVar is an integer, perhaps in your function definition:

String[] selectRowById ( int untrustedVar ) { ...

Or if it's a string, you might do this with a regex like:

Pattern valid_id_re = Pattern.compile('^\d{1,10}$');  // ensure id is between 1 and 10 billion
Matcher m = valid_id_re.matcher( unstrustedVar );
if ( ! m.matches() )
    return null;

But if you have longer inputs that do not have any grammar or structure guarantees (e.g., a web form textarea), then you will need to do the lower-level character replacements to escape the potentially bad characters. Per statement. Per variable. Per database flavor (PostgreSQL, Oracle, MySQL, SQLite, etc.). This ... is a can of worms.

The upside, of course, is that if you are not using prepared statements, and no one has yet done the work to otherwise avoid SQL injection attacks for your application, you have no where to go but up.

Meanwhile, I urge, urge, urge you to reconsider your stance on "We can't use prepared statements because reasons." More to the point, and as Gord Thompson correctly points out in the comments below, "it will be a considerable amount of work in any case, so why not just do it right and be done with it?"


Edit

After writing the above, it occurred to me that some might think that merely writing a prepared statement = better security. Actually, it's prepared statements with bound parameters that ups the security. For example, one could write this:

String sql = "SELECT * FROM mytable WHERE id = " + untrustedVar;
PreparedStatment pstmt = dbh.prepareStatement( sql );
return pstmt.executeQuery();

At this point, you've done little more than prepare a statement that has already been injected with malicious code. Instead, consider the actual binding of parameters:

String sql = "SELECT * FROM mytable WHERE id = ?";  // Raw string; never touched by "tainted" variable
PreparedStatment pstmt = dbh.prepareStatement( sql );
pstmt.setObject(1, p);  // Perform the actual binding.
return pstmt.executeQuery();

This latter example does two things. It first creates a known safe format, and sends that to the DB for preparation. Then, after the DB has returned a handle to the prepared statement, do we bind the variables, and finally execute the statement.

Community
  • 1
  • 1
hunteke
  • 3,648
  • 1
  • 7
  • 17