80

If yes, why are there still so many successful SQL injections? Just because some developers are too dumb to use parameterized statements?

kelunik
  • 6,274
  • 2
  • 37
  • 64
iceagle
  • 1,287
  • 2
  • 11
  • 13
  • 6
    This is a great question, with absolutely terrible answers (as the time i am commenting) – Ibu Jul 22 '11 at 05:47
  • I wish someone with a good 15k reputation at least or with good experience can add valuable input to this question. – Ibu Jul 22 '11 at 06:04
  • 4
    See [Bill Karwin's](http://stackoverflow.com/users/20860/bill-karwin) *Sql Injection Myths and Fallacies* [talk](http://www.ustream.tv/recorded/10772454) and [slides](http://www.slideshare.net/billkarwin/sql-injection-myths-and-fallacies) for more information on this subject. He explains what SQL injection is, how escaping is not usually good enough, and how stored procedures and parameterised statements can be compromised. – Mike Jul 22 '11 at 07:05
  • 2
    Also see some of [Bill Karwin's](http://stackoverflow.com/users/20860/bill-karwin#qpage_1-anpage_8-qsort_recent-ansort_votes) answers to similar questions: [What is SQL injection?](http://stackoverflow.com/questions/601300/what-is-sql-injection/2748346#2748346) – Mike Jul 22 '11 at 07:21

12 Answers12

69

When articles talk about parameterized queries stopping SQL attacks they don't really explain why, it's often a case of "It does, so don't ask why" -- possibly because they don't know themselves. A sure sign of a bad educator is one that can't admit they don't know something. But I digress. When I say I found it totally understandable to be confused is simple. Imagine a dynamic SQL query

sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password

so a simple sql injection would be just to put the Username in as ' OR 1=1-- This would effectively make the sql query:

sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password

This says select all customers where they're username is blank ('') or 1=1, which is a boolean, equating to true. Then it uses -- to comment out the rest of the query. So this will just print out all the customer table, or do whatever you want with it, if logging in, it will log in with the first user's privileges, which can often be the administrator.

Now parameterized queries do it differently, with code like:

sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'

parameters.add("User", username)
parameters.add("Pass", password)

where username and password are variables pointing to the associated inputted username and password

Now at this point, you may be thinking, this doesn't change anything at all. Surely you could still just put into the username field something like Nobody OR 1=1'--, effectively making the query:

sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'

And this would seem like a valid argument. But, you would be wrong.

The way parameterized queries work, is that the sqlQuery is sent as a query, and the database knows exactly what this query will do, and only then will it insert the username and passwords merely as values. This means they cannot effect the query, because the database already knows what the query will do. So in this case it would look for a username of "Nobody OR 1=1'--" and a blank password, which should come up false.

This isn't a complete solution though, and input validation will still need to be done, since this won't effect other problems, such as XSS attacks, as you could still put javascript into the database. Then if this is read out onto a page, it would display it as normal javascript, depending on any output validation. So really the best thing to do is still use input validation, but using parameterized queries or stored procedures to stop any SQL attacks.

Josip Ivic
  • 3,431
  • 8
  • 36
  • 54
  • 1
    This adds a lot to what I was looking for, but could you explain more on what you would do for "input validation?" You also mentioned that there are other attacks that could take place with the query, i.e., XSS, but could you explain how that would happen? So, essentially, how would we fully protect against SQL Injection, or are we loooking at all types of injection? thanks. – XaolingBao Jun 27 '16 at 19:48
  • 3
    @JosipIvic: Given how many people have asked how parameterized statements work, it's shocking to see so few- if any others, really- break down the answer like you did. Thanks for writing such a clear explanation with a pretty intuitive example. – daOnlyBG Mar 14 '17 at 15:03
  • Brilliant. An example paints a thousand words as they say! – Drenai Oct 29 '17 at 21:08
66

The links that I have posted in my comments to the question explain the problem very well. I've summarised my feelings on why the problem persists, below:

  1. Those just starting out may have no awareness of SQL injection.

  2. Some are aware of SQL injection, but think that escaping is the (only?) solution. If you do a quick Google search for php mysql query, the first page that appears is the mysql_query page, on which there is an example that shows interpolating escaped user input into a query. There's no mention (at least not that I can see) of using prepared statements instead. As others have said, there are so many tutorials out there that use parameter interpolation, that it's not really surprising how often it is still used.

  3. A lack of understanding of how parameterized statements work. Some think that it is just a fancy means of escaping values.

  4. Others are aware of parameterized statements, but don't use them because they have heard that they are too slow. I suspect that many people have heard how incredibly slow paramterized statements are, but have not actually done any testing of their own. As Bill Karwin pointed out in his talk, the difference in performance should rarely be used as a factor when considering the use of prepared statements. The benefits of prepare once, execute many, often appear to be forgotten, as do the improvements in security and code maintainability.

  5. Some use parameterized statements everywhere, but with interpolation of unchecked values such as table and columns names, keywords and conditional operators. Dynamic searches, such as those that allow users to specify a number of different search fields, comparison conditions and sort order, are prime examples of this.

  6. False sense of security when using an ORM. ORMs still allow interpolation of SQL statement parts - see 5.

  7. Programming is a big and complex subject, database management is a big and complex subject, security is a big and complex subject. Developing a secure database application is not easy - even experienced developers can get caught out.

  8. Many of the answers on stackoverflow don't help. When people write questions that use dynamic SQL and parameter interpolation, there is often a lack of responses that suggest using parameterized statements instead. On a few occasions, I've had people rebut my suggestion to use prepared statements - usually because of the perceived unacceptable performance overhead. I seriously doubt that those asking most of these questions are in a position where the extra few milliseconds taken to prepare a parameterized statement will have a catastrophic effect on their application.

Mike
  • 20,127
  • 2
  • 38
  • 65
10

Well good question. The answer is more stochastic than deterministic and I will try to explain my view, using a small example.

There many references on the net that suggest us to use parameters in our queries or to use stored procedure with parameters in order to avoid SQL Injection (SQLi). I will show you that stored procedures (for instance) is not the magic stick against SQLi. The responsibility still remains on the programmer.

Consider the following SQL Server Stored Procedure that will get the user row from a table 'Users':

create procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
           'from Users '+
           'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)

You can get the results by passing as parameters the username and the password. Supposing the password is in free text (just for simplicity of this example) a normal call would be:

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [dbo].[getUser] 
   @name = 'admin'
  ,@pass = '!@Th1siSTheP@ssw0rd!!'
GO

But here we have a bad programming technique used by the programmer inside the stored procedure, so an attacker can execute the following:

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [TestDB].[dbo].[getUser] 
   @name = 'admin'
  ,@pass = 'any'' OR 1=1 --'
GO

The above parameters will be passed as arguments to the stored procedure and the SQL command that finally will be executed is:

select usrID, usrUName, usrFullName, usrRoleID 
from Users 
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'

..which will get all rows back from users

The problem here is that even we follow the principle "Create a stored procedure and pass the fields to search as parameters" the SQLi is still performed. This is because we just copy our bad programming practice inside the stored procedure. The solution to the problem is to rewrite our Stored Procedure as follows:

alter procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID 
from Users 
where usrUName = @name and usrPass = @pass

What I am trying to say is that the developers must learn first what an SQLi attack is and how can be performed and then to safeguard their code accordingly. Blindly following 'best practices' is not always the safer way... and maybe this is why we have so many 'best practices'- failures!

Andreas Venieris
  • 443
  • 3
  • 14
  • I can understand your point and i am guilty of this. Sometimes there is a need for dynamic sql query creation which I used concatenation of parameters. How would you suggest I go about it? – TheProvost Oct 15 '15 at 07:28
  • @TheProvost that's a good question. Consider sp_executesql: https://msdn.microsoft.com/en-us/library/ms188001.aspx – Tim Oct 15 '15 at 10:12
  • @Tim Hi tim. Im new to dynamic sql. What is the difference between sp_executesql and EXECUTE(@SqlQuery) – TheProvost Oct 15 '15 at 10:31
  • 2
    i think this post explains a simple example well: http://www.codeproject.com/Tips/586207/How-to-prevent-SQL-Injection-in-Stored-Procedures -- but basically, EXECUTE(@SqlQuery) does nothing to prevent sql injection,however sp_executesql(@SqlQuery, ..., ...) does prevent it. The examples in the microsoft article should help. – Tim Oct 15 '15 at 10:58
  • Tim has the solution TheProvost... ;) You can use sp_executesql(@QUERY, @PARAMETERS, @VARS)... for a dynamic SQL case... ;) – Andreas Venieris Oct 16 '15 at 08:42
5

Yes, the use of prepared statements stops all SQL injections, at least in theory. In practice, parameterized statements may not be real prepared statements, e.g. PDO in PHP emulates them by default so it's open to an edge case attack.

If you're using real prepared statements, everything is safe. Well, at least as long as you don't concatenate unsafe SQL into your query as reaction to not being able to prepare table names for example.

If yes, why are there still so many successful SQL injections? Just because some developers are too dumb to use parameterized statements?

Yes, education is the main point here, and legacy code bases. Many tutorials use escaping and those can't be easily removed from the web, unfortunately.

Community
  • 1
  • 1
kelunik
  • 6,274
  • 2
  • 37
  • 64
  • 1
    The linked answer has nothing to do with prepared statements actually. – Your Common Sense Oct 09 '15 at 09:39
  • 1
    @YourCommonSense: It's about parameterized queries, they may not be actual prepares but emulated depending on the used driver. It's important to know and very much connected... – kelunik Oct 09 '15 at 10:24
  • 1
    Other answer in the same page have a very good comment: "If ALL your queries are parametrized, you're also protected against 2nd order injection. 1st order injection is forgetting that user data is untrustworthy. 2nd order injection is forgetting that database data is untrustworthy (because it came from the user originally)." – Rodrigo Oct 09 '15 at 14:40
  • @kelunik the linked answer isn't about parameterized queries either, it's about a library that essentially fakes them. A parameterized query is one that is sent to the server with separate parameter values. – Panagiotis Kanavos Oct 15 '15 at 12:32
  • @PanagiotisKanavos: I know the content of that answer pretty well. It's just an example (and a pretty common one) that the parameterized queries you use may not actually be implemented as prepared statements... – kelunik Oct 15 '15 at 12:39
3

I avoid absolutes in programming; there is always an exception. I highly recommend stored procedures and command objects. A majority of my back ground is with SQL Server, but I do play with MySql from time to time. There are many advantages to stored procedures including cached query plans; yes, this can be accomplished with parameters and inline SQL, but that opens up more possibilities for injection attacks and doesn't help with separation of concerns. For me it's also much easier to secure a database as my applications generally only have execute permission for said stored procedures. Without direct table/view access it's much more difficult to inject anything. If the applications user is compromised one only has permission to execute exactly what was pre-defined.

My two cents.

Derek
  • 41
  • 2
  • How does this relate to the question? How are you going to call and pass parameters to the stored procedure? Using string concatenation or by using a parameterized query? Besides - what if someone uses string concatenation *inside* the stored procedure to create a "dynamic" query? Just because it's a stored procedure doesn't mean it's safer – Panagiotis Kanavos Oct 15 '15 at 12:27
  • Generally I use a command object and I also avoid running "dynamic queries" by design. – Derek Oct 19 '15 at 14:51
2

Can parameterized statement stop all SQL injection?

Yes, as long as your database driver offers a placeholder for the every possible SQL literal. Most prepared statement drivers don't. Say, you'd never find a placeholder for a field name or for an array of values. Which will make a developer to fall back into tailoring a query by hand, using concatenation and manual formatting. With predicted outcome.

That's why I made my Mysql wrapper for PHP that supports most of literals that can be added to the query dynamically, including arrays and identifiers.

If yes, why are there still so many successful SQL injections? Just because some developers are too dumb to use parameterized statements?

As you can see, in reality it's just impossible to have all your queries parameterized, even if you're not dumb.

Your Common Sense
  • 152,517
  • 33
  • 193
  • 313
  • If ALL your queries are parameterized (come they from user data or from your database data), then it seems you're protected, as stated in the most voted comment here: http://stackoverflow.com/a/134138/1086511 – Rodrigo Oct 09 '15 at 14:43
  • I kind of asked for your opinion, just because you seemed reasonable enough. I don't think your method will be the best if what I've read elsewhere holds. Anyway, I'll love if you improve on "with regular tools you cannot have ALL your queries parameterized". – Rodrigo Oct 10 '15 at 05:13
  • I started to read your answer, till I get to the idea of identifying "SQL literals". The idea didn't seem quite right to me (it seemed overjob). If it's true that parameterized queries avoid injection in PHP (I'm still researching), then my next step is to avoid javascript injections. Then, I'll come back to study your solution. Also, I'm using postgres, and maybe your solution is mysql specific? – Rodrigo Oct 10 '15 at 05:40
  • Ok, now I read it (again), and don't think "it's just impossible to have all your queries parameterized" is an improvement. Is it impossible in MySQL? Is it impossible also in PostgreSQL? Why? Is there any query outside my php script? Where? I think by identifier you mean a reserved word that you try to strip out of your $_POST array? This seems not the way to go, to me (intuitively, I may be wrong, of course). Also, I didn't understand the "Did you try to bind it ever?" Bind what? – Rodrigo Oct 10 '15 at 05:47
  • That's not so easy to find on the web as I thought. Please add a reference if you can. – Rodrigo Oct 10 '15 at 05:56
2

First my answer to your first question: Yes, as far as I know, by using parameterized queries, SQL injections will not be possible anymore. As to your following questions, I am not sure and can only give you my opinion on the reasons:

I think it's easier to "just" write the SQL query string by concatenate some different parts (maybe even dependent on some logical checks) together with the values to be inserted. It's just creating the query and executing it. Another advantage is that you can print (echo, output or whatever) the sql query string and then use this string for a manual query to the database engine.

When working with prepared statements, you always have at least one step more: You have to build your query (including the parameters, of course) You have to prepare the query on the server You have to bind the parameters to the actual values you want to use for your query You have to execute the query.

That's somewhat more work (and not so straightforward to program) especially for some "quick and dirty" jobs which often prove to be very long-lived...

Best regards,

Box

TomS
  • 448
  • 8
  • 22
2

SQL injection is a subset of the larger problem of code injection, where data and code are provided over the same channel and data is mistaken for code. Parameterized queries prevent this from occurring by forming the query using context about what is data and what is code.

In some specific cases, this is not sufficient. In many DBMSes, it's possible to dynamically execute SQL with stored procedures, introducing a SQL injection flaw at the DBMS level. Calling such a stored procedure using parameterized queries will not prevent the SQL injection in the procedure from being exploited. Another example can be seen in this blog post.

More commonly, developers use the functionality incorrectly. Commonly the code looks something like this when done correctly:

db.parameterize_query("select foo from bar where baz = '?'", user_input)

Some developers will concatenate strings together and then use a parameterized query, which doesn't actually make the aforementioned data/code distinction that provides the security guarantees we're looking for:

db.parameterize_query("select foo from bar where baz = '" + user_input + "'")

Correct usage of parameterized queries provides very strong, but not impenetrable, protection against SQL injection attacks.

2

I wouldn't say "dumb".

I think the tutorials are the problem. Most SQL tutorials, books, whatever explain SQL with inlined values, not mentioning bind parameters at all. People learning from these tutorials don't have a chance to learn it right.

Markus Winand
  • 7,537
  • 1
  • 28
  • 39
  • 2
    It's not enought. Why people don't use framework or some orm? Why they don't test for "dumb injection" with some stupid tester? Because sometimes the boss don't pay you well or he pays you X moneys for a project and you need to run from a project to another project and from one to another one to get some money. You must be faster and faster. The coder is stressed and over pressed, so the code works but it's bad written. – jedi Oct 12 '15 at 22:10
2

Because most code isn't written with security in mind, and management, given a choice between adding features (especially something visible that can be sold) and security/stability/reliability (which is a much harder sell) they will almost invariably choose the former. Security is only a concern when it becomes a problem.

evil otto
  • 9,592
  • 21
  • 37
1

even if prepared statements are properly used throughout the web application’s own code, SQL injection flaws may still exist if database code components construct queries from user input in an unsafe manner. The following is an example of a stored procedure that is vulnerable to SQL injection in the @name parameter:

CREATE PROCEDURE show_current_orders
(@name varchar(400) = NULL)
AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ‘SELECT id_num, searchstring FROM searchorders WHERE ‘ +
‘searchstring = ‘’’ + @name + ‘’’’;
EXEC (@sql)
GO

Even if the application passes the user-supplied name value to the stored procedure in a safe manner, the procedure itself concatenates this directly into a dynamic query and therefore is vulnerable.

Artinium
  • 455
  • 5
  • 7
1

To protect your application from SQL injection, perform the following steps:

Step 1. Constrain input. Step 2. Use parameters with stored procedures. Step 3. Use parameters with dynamic SQL.

Refer to http://msdn.microsoft.com/en-us/library/ff648339.aspx

Fahad Hussain
  • 1,145
  • 1
  • 10
  • 17
  • 8
    Stored procedures alone are not actually a help. It's possible to construct query strings dynamically in a stored procedure, just as in the client code. – Phil Miller Jul 22 '11 at 05:42
  • @Fahad I might reword #2 as "Use parameterized statements in queries and in stored procedures." +1 to Novelocrat's comment that using stored procedures without parameters doesn't get you much. – Matthew Sep 09 '16 at 23:30