|
|
Subscribe / Log in / New account

SQL injection attacks

March 24, 2006

This article was contributed by Jake Edge.

One of the more devastating attacks on a web application is also one of the most common: SQL injection. This technique allows an attacker to gain access to the database that underlies many web sites and read and potentially modify data that is not meant to be available to users of that site. This article provides an overview of how SQL injection works and what can be done to avoid it.

A classic example of SQL injection starts with a query that looks something like:

    SELECT id FROM users WHERE name='$name' AND pass='$pass';
This query might be used to authenticate users when they log in to a web site. If it returns a row, the user id returned is considered to be authenticated and the application proceeds to serve the correct page for that user. In this case, the $name and $pass variables would come from a login form that might look something like:
    <form method="post" action="login.php">
        <input type="text" name="name">
        <input type="password" name="pass">
        <input type="submit" value="login">
    </form>

If the login.php program in this example blindly sets the variables to the values that come from the user, a malicious user can bypass the authentication. Consider the following inputs:

    $user = "' OR 1=1 ";
    $pass = "' OR 1=1 LIMIT 1";
This results in a query that is completely different from what the web programmer expected:
    SELECT id FROM users WHERE name='' OR 1=1 AND pass='' OR 1=1 LIMIT 1;
This query will always return one row (unless the table is empty) and it is likely to be the first entry in the table. For many applications, that entry is the administrative login; the one with the most privileges.

This simple example barely scratches the surface of the kinds of attacks that can be made using SQL injection. Depending on the DBMS, it may be possible to do multiple queries via an injection by separating each with a semicolon:

    SELECT id FROM users WHERE name='' AND pass=''; DROP TABLE users;
which is, of course, a rather destructive injection. MySQL does not allow multiple queries in a statement, but PostgreSQL is susceptible to this technique.

Web site and/or database search functions are particularly dangerous because they display their output; if a malicious user can inject any query they choose, they can capture the entire contents of the database. The UNION keyword can turn a query such as:

    SELECT city, state FROM users WHERE name LIKE '%$search%';
into:
    SELECT city, state FROM users 
           WHERE name LIKE '%%' UNION 
                 SELECT name, pass FROM users 
			WHERE name LIKE '%%';
And instead of just printing the city and state of users that match the input string, we are also printing the username and password of every user in the system.

A certain amount of guessing column names and types is required if an attacker does not have access to the database schema, but they are often not very hard to guess given some understanding of the application. Some database systems, notably Microsoft SQL Server, seem to deliberately shoot themselves in the foot by providing the schema for all tables in a generally accessible database, thereby removing all the guesswork.

Injection also requires a certain amount of imagination to visualize the kinds of queries that might be going on behind the input boxes of a web form. It requires quite a bit of trial and error unless one has access to the source; this is why the majority of reported SQL injections are in free software or open source web applications.

Note that it is not only web forms using the POST method that are vulnerable, many web applications that use the GET method are vulnerable to injections via the URL:

    http://vulnerablewebapp.com/login.php?\
           name=%27%20OR%201%3D1%20&pass=%27%20OR%201%3D1%20LIMIT%201

Like many other web vulnerabilities, SQL injection stems from insufficient filtering of user input. Unfortunately, it is sometimes difficult to determine what kinds of input should be accepted (for example the password "' OR 1=1" would not necessarily seem illegal) and using various filtering functions provided by the language may not actually prevent injections. The PHP addslashes() function is often used to sanitize user input because it will put a backslash in front of single quotes which will stop the kinds of injections described above. Unfortunately, there are techniques to circumvent this particular 'fix' as well.

Probably the simplest way to protect queries from SQL injection is by using prepared statements with placeholders. Any reasonable database interface will provide a way to use this functionality and in many cases, it is fairly portable between languages and DBM systems.

Instead of directly interpolating string values into query strings, a query is prepared using '?' as a placeholder for the variables as shown in the following pseudocode:

    $sth = prepare("SELECT id FROM users WHERE name=? AND pass=?");
    execute($sth, $name, $pass);
This has a number of advantages: the DBMS library is responsible for properly quoting the values and because of the way the variables are bound to the query, they can never be treated as anything other than data for the particular place they have in the prepared statement. This effectively turns the injection attempt above into a query like:
    SELECT id FROM users WHERE name='\' OR 1=1 ' AND pass='\' OR 1=1 LIMIT 1';
which is unlikely to authenticate.

Another way to defend against injections is by ensuring that all user input is passed through a database specific quoting function before being used in a query:

    $name = db_quote($name);
    $pass = db_quote($pass);
    SELECT id FROM users WHERE name=$name AND pass=$pass;
Depending on the language and database API, this method may also be fairly portable.

The final recommended technique is also the most complicated; but it can provide an additional level of security if stored procedures are available for the DBMS. Stored procedures are queries (and more complicated functions) that are created by the database administrator and stored with the database. These procedures are then called by the application code to do any queries that they require. The equivalent of the prepare functionality is done on the procedures at the time they are stored and with proper coding, this will prevent injections. One of the main advantages is that these procedures run with the privileges of the user that stored them, instead of the user invoking them and this allows the application to have a much more limited set of privileges than it would normally require. The upshot is that it can protect the database from reading or writing even if the application is subverted in some way.

SQL injections are clearly a serious security problem, but one that can be thwarted relatively easily once one understands the problem and the ways to program around it.


Index entries for this article
GuestArticlesEdge, Jake


to post comments

SQL injection attacks

Posted Mar 30, 2006 2:28 UTC (Thu) by jwb (guest, #15467) [Link] (11 responses)

Another great article. One of the many horrors of MS-SQL is the incredible amount of functionality
available to a SQL injection attacker. MS-SQL can be made to open a connection to any other
database, even on other hosts or networks. A SQL injection attack against MS-SQL can allow the
attacker to tell your database to connect to any random instance of SQL Server and replicate itself.
This obviously takes all the guesswork out of trying to reverse engineer the schema. An attacker
can rip off an entire MS-SQL instance with a single HTTP request.

Guess work?

Posted Mar 30, 2006 23:49 UTC (Thu) by GreyWizard (guest, #1026) [Link] (8 responses)

s/guess work/security through obscurity/

Database features do not excuse sloppy applications.

Guess work?

Posted Mar 31, 2006 6:39 UTC (Fri) by hppnq (guest, #14462) [Link] (7 responses)

Basically, you are suggesting that in order to kill a fly, you should use a proper gun, practice at a firing range, isolate the fly in a safe environment, surgically optimize your eye-hand coordination, calculate environmental influences, suppress urges to start shooting at random, before taking a shot at removing the annoying intruder.

Sure, if a gun is my only tool, I'd go through all that. But I'd rather whack the bastard with a newspaper.

Nonsense

Posted Mar 31, 2006 15:07 UTC (Fri) by GreyWizard (guest, #1026) [Link] (6 responses)

That is a perfectly ridiculous analogy. Whining that the database has too many features that might be useful for someone exploiting SQL injection vulnerabilities in an unrelated application is not so much swatting the fly as cursing the publisher for printing a newspaper that's too hard to swing while the thing is still buzzing around your head.

Nonsense

Posted Mar 31, 2006 15:36 UTC (Fri) by hppnq (guest, #14462) [Link] (5 responses)

So what are you suggesting then? That we should all write perfect code? Yes, that would solve the problem. Is it realistic? Not a chance in hell.

Most or all security implementations heavily depend on defining proper interfaces to resources and making sure that access to resources is only possible through these interfaces.

It follows quite simply that it's wise to start off with as little resources and interfaces as possible if you care about security.

RTFA

Posted Mar 31, 2006 22:17 UTC (Fri) by GreyWizard (guest, #1026) [Link] (4 responses)

Contrary to your raving, filtering user input does not require perfect code. I suggest reading the article to which this thread is attached. There you will find suggestions such as using prepared statements or stored procedures. As stated above, "SQL injections [...] can be thwarted relatively easily once one understands the problem and the ways to program around it." On the other hand, no database can provide protection from gaping security holes in external applications.

Mmmmhh

Posted Apr 1, 2006 8:53 UTC (Sat) by hppnq (guest, #14462) [Link] (3 responses)

It seems to me that we are making a lot of fuss about something that we basically feel the same about. If you take the time to calm down and read the comments as well as the article you might see this too.

Now, you were the one that brought up the topics of sloppy programming and "security through obscurity", taking this discussion explicitly to the realm of the real world, where the perfect solution does not exist. You observed that database features are no excuse for bad programming, while I am of the opinion that they should not be an excuse.

In the real world resources are limited. At some point a decision will have to be made: is it good enough? Since security means nothing in the laboratory, and everything in the real world, this is a very important observation. This is also why I mention writing perfect code: it cannot be done, and the only way to avoid having to make suboptimal decisions is to remove the necessity of making those decisions. This is a classic trade-off between security and functionality.

Instead of having to protect features one does not need, it is better to not have them available in the first place. That of course leaves more resources available to get the actual job done: defining the correct interfaces to the functionality you want to provide or use and protecting those interfaces properly.

On the other hand, no database can provide protection from gaping security holes in external applications.

This is the same problem. Do take some time to think about it.

Practice What You Preach

Posted Apr 3, 2006 15:50 UTC (Mon) by GreyWizard (guest, #1026) [Link] (2 responses)

You reply to a comment about security through obscurity with an irrelevant analogy to shooting mosquitoes, and now you accuse me of not reading what I reply to? You rant and rave about the impossibility of perfect code, and now you tell me to calm down? Amusing. But your airy hand waving about "protecting features one doesn't need" still misses the point: using the dumbest database available would be a trade-off between security and functionality only if this were an effective substitute for plugging SQL injection holes in the application. As long as there are remote exploits the application cannot meet even the least demanding security requirements with any database.

This is really not so complicated. Practice what you preach, especially with regard to taking the time to think about it.

Practice What You Preach

Posted Apr 3, 2006 22:19 UTC (Mon) by hppnq (guest, #14462) [Link] (1 responses)

Well, I just tried to add some more perspective to your rather simplistic "thou shalt not program sloppily" statement. It appears to me that in your enthusiasm to slight me, you seem to miss your own point completely.

*plonk*

Perspective Indeed

Posted Apr 4, 2006 2:58 UTC (Tue) by GreyWizard (guest, #1026) [Link]

You are confused. "Database features do not excuse sloppy applications" is simple. "Thou shalt not program sloppily" is simplistic. The latter is your contribution, not mine. Rambling about mosquitoes, whining about perfect code, splitting hairs over "are" and "should" and pretending I don't understand my own point is your idea of adding perspective, is it? Spare me such generosity.

SQL injection attacks

Posted Mar 31, 2006 11:06 UTC (Fri) by pdc (guest, #1353) [Link] (1 responses)

To try to avoid this we do all access from a web app to the SQL Server database via stored procedures, with user input passed as parameters. At least then you can reastrict the privileges of the web application to just the procedures it needs to use. Makes the database development rather tedious, however.

SQL injection attacks

Posted Mar 31, 2006 19:20 UTC (Fri) by dwkunkel (guest, #5999) [Link]

I use Oracle stored procedures to simplify my web applications. All the business logic is handled by stored procedures and there is no sql in the web pages. The pages just pass parameters to the appropriate stored procedure.

An Oracle stored procedure can return multiple reference cursors that can be cast to Java ResultSets and used directly in a web page. The reference cursors can also be converted to Cached RowSets and used in Data Transfer Objects. Performance is quite good because everything is done in a single trip to the database.

I don't know about SQL Server, but I find Oracle's PL/SQL to be an easy to use programming language that makes it relatively simple to produce very readable code.

CHAR() + mysql for injection?

Posted Mar 30, 2006 8:21 UTC (Thu) by wingo (guest, #26929) [Link]

I hadn't heard of the CHAR() injection strategy briefly mentioned in the article. Google is not being helpful about it. Anyone have more information on that one? It sounds particularly nasty.

SQL injection attacks

Posted Mar 30, 2006 10:14 UTC (Thu) by NAR (subscriber, #1313) [Link] (1 responses)

Great article. I've worked on a project which displayed the results of an SQL query in a HTML page and once I played with inserting HTML code into the database to break the output. However, I didn't try to break the SQL insert commands. I think I've just found an interesting pet project for the afternoon :-)

Bye,NAR

SQL injection attacks

Posted Mar 31, 2006 15:46 UTC (Fri) by hppnq (guest, #14462) [Link]

Cancel your dinner reservations, NAR. ;-)

quoting

Posted Mar 30, 2006 14:20 UTC (Thu) by rfunk (subscriber, #4054) [Link]

It's important to note that the details of quoting strings are DBMS-dependent, and PHP's addslashes() is insufficient (or in some cases just plain wrong). Some of the comments on the PHP addslashes() doc page go into the details. It's always better to use a DBMS-specific quoting function (e.g. mysql_real_escape_string) than to blindly add backslashes. Which is why PHP's "magic quotes" feature is so annoyingly useless.

This tendency to SQL injection is one of the reasons people see PHP as an inherently insecure language, or at least one that encourages insecure programming.

input filtering

Posted Mar 30, 2006 14:51 UTC (Thu) by ccyoung (guest, #16340) [Link]

the function db_quote() for input filtering is in my experience inadequate.

what is needed is one filter function for each data type. this not only formats but does type checking. for example, db_get_string may not allow quotes and punctuation, whereas db_get_text might be more forgiving.

db_get_code( $code, $mustexist=false )
db_get_string( $str, $mustexist=false )
db_get_text( $text, $mustexist=false )
...
db_get_integer( $int, $mustexist=false )

a big gotcha in PHP is it's confusion between 0, null, and an empty string.

SQL injection attacks

Posted Mar 30, 2006 17:55 UTC (Thu) by iabervon (subscriber, #722) [Link]

The prepared statement functionality, at least in some databases, is actually even better than this article suggests: it causes the statement with the question marks to be parsed to generate the sequence of database-internal operations which will be performed. Then when the arguments are filled, it doesn't need to escape the strings, because it doesn't unescape them; it doesn't treat any characters specially at this point. Furthermore, since all of the parsing is already done when user input comes in, it can't be induced to perform unexpected operations, because the sequence of operations it will perform is already determined at this point.

Furthermore, it's often faster, because it can cache the execution plan for the query, because all of the "SELECT id FROM users WHERE user=? AND pass=?" parts are identical, and the parsing can be a significant portion of the query time, since there's a bunch of effort in figuring out what's going on, and that the useful optimization for this query is the unique index on users.user.

SQL injection attacks

Posted Mar 30, 2006 19:10 UTC (Thu) by yodermk (guest, #3803) [Link] (1 responses)

Great article! However, given this:

SELECT id FROM users WHERE name='$name' AND pass='$pass';

the shown "modified" query:

SELECT id FROM users WHERE name='' OR 1=1 AND pass='' OR 1=1 LIMIT 1;

does not show the end quote (') after $name and $pass. Would it not translate to this:

SELECT id FROM users WHERE name='' OR 1=1' AND pass='' OR 1=1 LIMIT 1';

which would be an SQL error? Or am I missing something???

SQL injection attacks

Posted Mar 30, 2006 19:54 UTC (Thu) by jake (editor, #205) [Link]

> SELECT id FROM users WHERE name='' OR 1=1' AND pass='' OR 1=1 LIMIT 1';

> which would be an SQL error? Or am I missing something???

No, nice catch.

$user = "' OR 1=1 OR name='";
$pass = "' OR 1=1 LIMIT 1 --";

should do the trick ...

jake

SQL injection attacks

Posted Mar 31, 2006 16:56 UTC (Fri) by dps (guest, #5725) [Link] (1 responses)

There is a simpler fix, which also stops other attacks too... do proper input validation---if something is suppsesd to be a number, make sure it really is. Numbers like "1 OR 1=1" (without the quotes) can do evil things in contexts expecting numbers. Input validation stops that too.

Using magic_quote_qpc, SQL syntax randomisation, etc are all useful backstops in case you somehow fail to properly validate something. I have my doutbs about the security of stored procedures when fed evil input, unless handling it safely is a primary design goal.

Sadly there is no agreement about parameters in prepared statements. ODBC and MySQL wants ?. Postgresql want $1, $2, etc and oracle accept $<almost anythihg>. (This sort of thing is one of the "joys" of writng multiple database server SQL.)

SQL injection attacks

Posted Apr 1, 2006 13:35 UTC (Sat) by holstein (guest, #6122) [Link]

As other have pointed out, '1 OR 1=1' can be a valid password. Or at least, a validating routine for valid password should accept this; something like '1hotguys = 1' is a valid password...

So, input validation is not a silver bullet; it's just a step in the journey.

Using a stored procedure help because the data will be used just as it is: data. You can't turn the input in a variation of the execute query (at least, note easily). Of course, bad data could trigger other kind of problems (like, say, a buffer overflow attack on the RDBMS).

A for the syntax for placeholders in prepared statements, with the Perl DBI at least, the syntax is the same for every RDBMS. And having worked with MySQL, Oracle and SQLite with PHP, I don't recall having seen different syntax used; maybe it was because I was not using the 'direct' API (like mysql_xx, oc8_xx, etc.) but instead using abstraction layer, like PEAR DB. But if you are writing multiple database server application, I would guess that this is a 'sine qua none' condition. At least for me!

SQL injection attacks

Posted Apr 7, 2006 9:33 UTC (Fri) by m.alessandrini (guest, #36991) [Link]

A very interesting review of internet application vulnerabilities can be found at http://www.owasp.org/documentation/topten.html (OWASP Top Ten Most Critical Web Application Security Vulnerabilities)


Copyright © 2006, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy