4

Background: I have started a project using JDBC and MYSQL to simulate a bookstore, all local. To connect to the database, I started out using a Statement but I began to read that when using a query multiple times that just changes its parameters, it can be more efficient to use a PreparedStatement for those queries. However, the thing advantage I read the most about was how PreparedStatements could prevent SQL injection much better.

Sources: Answers on this thread here
Google
Professors

My Question: How do PreparedStatements prevent SQL injection better, or even different for that matter, than Statements when dealing with parametrized queries? I am confused because, if I understand correctly, the values still get passed into the SQL statement that gets executed, it's just up to the the programmer to sanitize the inputs.

Community
  • 1
  • 1
Matt C
  • 4,470
  • 5
  • 26
  • 44
  • 4
    "it's just up to the the programmer to sanitize the inputs." What could possibly go wrong then? – Hauke Ingmar Schmidt Mar 10 '14 at 20:41
  • They are more secure because they use parameters. A prepared statement that was concatenated strings from code or the user would be totally pointless wouldn't it. You'd have to prepare it again every time arguments changed changed. – Tony Hopkinson Mar 10 '14 at 20:44
  • Read the content of the answer and try to follow links posted there. For example: [SQL Injection article from Wikipedia](http://en.wikipedia.org/wiki/SQL_injection) – Luiggi Mendoza Mar 10 '14 at 20:56
  • How is using parameters more secure? @TonyHopkinson – Matt C Mar 10 '14 at 21:08
  • Why do you not have to sanitize if you use PreparedStatements? @his – Matt C Mar 10 '14 at 21:11
  • Because they are sanitised automatically by the prepared statement code, you don't have to remember to do it. – Tony Hopkinson Mar 10 '14 at 21:15
  • What sanitizes them? @TonyHopkinson – Matt C Mar 10 '14 at 21:17
  • @MatthewC The implementation of `PreparedStatement` that the JDBC driver provides. I'll update my answer. – yshavit Mar 10 '14 at 21:18
  • The component, or library or other thingy that you are using AcmePreparedQuery.Parameters[0].value = "Select password From Users Where UserName = 'Administrator'" – Tony Hopkinson Mar 10 '14 at 21:19
  • That made no sense. The "other thingy"? And, I have no clue where your SQL statement came from. @TonyHopkinson – Matt C Mar 10 '14 at 21:21
  • It came from the user / hacker, they typed it in because you aren't using prepared statements, on the off chance you forgot to sanitise this one. – Tony Hopkinson Mar 10 '14 at 21:27
  • @TonyHopkinson, and my question is, how does using prepared statements make it so you don't have to sanitize? – Matt C Mar 14 '14 at 14:24
  • They call sanitize for you. So sanitisation still happens, but you do not have to remember to always make an explicit call yourself. – Tony Hopkinson Mar 14 '14 at 15:12
  • @TonyHopkinson, So why did it take you 10 comments to answer that simple question? Was it really that hard to not be a smartass and just answer a question for once? Anyway, what do you mean call sanitize? Is sanitize a method or maybe a class that it uses? Can you be a little more descriptive? – Matt C Mar 15 '14 at 15:26
  • Perhaps because it took that long for you to indicate what you didn't know... Sanitize is thing to do, how many methods it is and what they are called would depend on the code that makes up whatever function / component you are using.PS I'm a Brit which means I'm a smartarse... – Tony Hopkinson Mar 15 '14 at 18:26

3 Answers3

8

You're right that you could do all the sanitation yourself, and thus be safe from injection. But this is more error-prone, and thus less safe. In other words, doing it yourself introduces more chances for bugs that could lead to injection vulnerabilities.

One problem is that escaping rules could vary from DB to DB. For instance, standard SQL only allows string literals in single quotes ('foo'), so your sanitation might only escape those; but MySQL allows string literals in double quotes ("foo"), and if you don't sanitize those as well, you'll have an injection attack if you use MySQL.

If you use PreparedStatement, the implementation for that interface is provided by the appropriate JDBC Driver, and that implementation is responsible for escaping your input. This means that the sanitization code is written by the people who wrote the JDBC driver as a whole, and those people presumably know the ins and outs of the DB's specific escaping rules. They've also most likely tested those escaping rules more thoroughly than you'd test your hand-rolled escaping function.

So, if you write preparedStatement.setString(1, name), the implementation for that method (again, written by the JDBC driver folks for the DB you're using) could be roughly like:

public void setString(int idx, String value) {
    String sanitized = ourPrivateSanitizeMethod(value);
    internalSetString(idx, value);
}

(Keep in mind that the above code is an extremely rough sketch; a lot of JDBC drivers actually handle it quite differently, but the principle is basically the same.)

Another problem is that it could be non-obvious whether myUserInputVar has been sanitized or not. Take the following snippet:

private void updateUser(int name, String id) throws SQLException {
    myStat.executeUpdate("UPDATE user SET name=" + name + " WHERE id=" + id);
}

Is that safe? You don't know, because there's nothing in the code to indicate whether name is sanitized or not. And you can't just re-sanitize "to be on the safe side", because that would change the input (e.g., hello ' world would become hello '' world). On the other hand, a prepared statement of UPDATE user SET name=? WHERE id=? is always safe, because the PreparedStatement's implementation escapes the inputs before it plugs values into the ?.

yshavit
  • 42,327
  • 7
  • 87
  • 124
  • You say do it yourself, but who else is going to do it? And the snipped you provided could just as well be used with prepared statements like this: `private void executePrepStat(PID_ADDUSER, String name, int id) { prepStat[ PID_ADDUSER ].setString(1, name); prepStat[ PID_ADDUSER ].setInt(2, id); } ` And do you know if that name or id is safe? No, so what is the difference? – Matt C Mar 10 '14 at 21:13
  • executePrepStat does it for you! – Tony Hopkinson Mar 10 '14 at 21:28
  • specifically, `prepStat.setString` does it for you. – yshavit Mar 10 '14 at 21:30
  • 1
    Actually most drivers do not escape the parameters: parameters are usually sent separate from the statement and therefor do not need to be escaped. – Mark Rotteveel Mar 11 '14 at 08:49
1

When using a PreparedStatement the way it is meant to be used - with a fixed query text with parameter placeholders, no concatenation of external values -, then you are protected against SQL Injection.

There are roughly two ways this protection works:

  1. The JDBC driver properly escapes the values and inserts them in the query at the placeholder positions, and sends the finished query to the server (AFAIK only MySQL Connector/J does this, and only with useServerPrepStmts=false which is the default).

  2. The JDBC driver sends the query text (with placeholders) to the server, the server prepares the query and sends back a description of the parameters (eg type and length). The JDBC driver then collects the parameter values and sends these as a block of parameter values to the server. The server then executes the prepared query using those parameter values.

Given the way a query is prepared and executed by the server, SQL injection cannot occur at this point (unless of course you execute a stored procedure, and that stored procedure creates a query dynamically by concatenation).

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
0

The framework , Sql driver makes sure to escape the input. If you use string Statements and escape properly - will achieve same result. But that is not recommended as Preparend statements seem like more lines of code but lead to more structured code as well. Instead of a soup of long sql lines.

Plus since we set each parameter separately and explicitly the underlying driver class can escape them correctly depending on the data base in use. Meaning you could change the data base by config, but no matter the driver takes care of escaping. So one data base might need slashes escaped and another might want two single quotes ...

This also leads to less code as you do not need to bother about this. Simply put you let the framework / common classes one level below the app code take care of it.

tgkprog
  • 4,493
  • 4
  • 41
  • 70