Anda di halaman 1dari 13

An SQL Injection can destroy your database.

SQL in Web Pages


In the previous chapters, you have learned to retrieve (and update) database data, using SQL.
When SQL is used to display data on a web page, it is common to let web users input their
own search values.
Since SQL statements are text only, it is easy, with a little piece of computer code, to
dynamically change SQL statements to provide the user with selected data:

Server Code
txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;
The example above, creates a select statement by adding a variable (txtUserId) to a select
string. The variable is fetched from the user input (Request) to the page.
The rest of this chapter describes the potential dangers of using user input in SQL statements.

SQL Injection
SQL injection is a technique where malicious users can inject SQL commands into an SQL
statement, via web page input.
Injected SQL commands can alter SQL statement and compromise the security of a web
application.

SQL Injection Based on 1=1 is Always True


Look at the example above, one more time.
Let's say that the original purpose of the code was to create an SQL statement to select a user
with a given user id.
If there is nothing to prevent a user from entering "wrong" input, the user can enter some
"smart" input like this:
UserId:
105 or 1=1

Server Result
SELECT * FROM Users WHERE UserId = 105 or 1=1
The SQL above is valid. It will return all rows from the table Users, since WHERE 1=1 is
always true.
Does the example above seem dangerous? What if the Users table contains names and
passwords?
The SQL statement above is much the same as this:
SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1
A smart hacker might get access to all the user names and passwords in a database by simply
inserting 105 or 1=1 into the input box.

SQL Injection Based on ""="" is Always True


Here is a common construction, used to verify user login to a web site:
User Name:

Password:

Server Code
uName = getRequestString("UserName");
uPass = getRequestString("UserPass");
sql = "SELECT * FROM Users WHERE Name ='" + uName + "' AND Pass ='" + uPass + "'"
A smart hacker might get access to user names and passwords in a database by simply
inserting " or ""=" into the user name or password text box.
The code at the server will create a valid SQL statement like this:

Result
SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""
The result SQL is valid. It will return all rows from the table Users, since WHERE ""="" is
always true.

SQL Injection Based on Batched SQL Statements


Most databases support batched SQL statement, separated by semicolon.

Example
SELECT * FROM Users; DROP TABLE Suppliers
The SQL above will return all rows in the Users table, and then delete the table called
Suppliers.
If we had the following server code:

Server Code
txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;
And the following input:
User id:
105; DROP

The code at the server would create a valid SQL statement like this:

Result
SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers

Parameters for Protection


Some web developers use a "blacklist" of words or characters to search for in SQL input, to
prevent SQL injection attacks.
This is not a very good idea. Many of these words (like delete or drop) and characters (like
semicolons and quotation marks), are used in common language, and should be allowed in
many types of input.
(In fact it should be perfectly legal to input an SQL statement in a database field.)
The only proven way to protect a web site from SQL injection attacks, is to use SQL
parameters.
SQL parameters are values that are added to an SQL query at execution time, in a controlled
manner.

ASP.NET Razor Example


txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = @0";
db.Execute(txtSQL,txtUserId);
Note that parameters are represented in the SQL statement by a @ marker.
The SQL engine checks each parameter to ensure that it is correct for its column and are
treated literally, and not as part of the SQL to be executed.

Another Example
txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
db.Execute(txtSQL,txtNam,txtAdd,txtCit);
You have just learned to avoid SQL injection. One of the top website vulnerabilities.

Examples
The following examples shows how to build parameterized queries in some common web
languages.
SELECT STATEMENT IN ASP.NET:
txtUserId = getRequestString("UserId");
sql = "SELECT * FROM Customers WHERE CustomerId = @0";
command = new SqlCommand(sql);
command.Parameters.AddWithValue("@0",txtUserID);
command.ExecuteReader();
INSERT INTO STATEMENT IN ASP.NET:
txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
command = new SqlCommand(txtSQL);
command.Parameters.AddWithValue("@0",txtNam);
command.Parameters.AddWithValue("@1",txtAdd);
command.Parameters.AddWithValue("@2",txtCit);
command.ExecuteNonQuery();
INSERT INTO STATEMENT IN PHP:

$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)


VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd);
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();

A SQL injection attack is exactly what the name suggests it is where a hacker tries to
inject his harmful/malicious SQL code into someone elses database, and force that
database to run his SQL. This could potentially ruin their database tables, and even extract
valuable or private information from their database tables. The idea behind SQL injection is
to have the application under attack run SQL that it was never supposed to run. How do
hackers do this? As always, its best to show this with examples that will act as a tutorial on
SQL injection.

SQL Injection Example


In this tutorial on SQL injection, we present a few different examples of SQL injection
attacks, along with how those attacks can be prevented. SQL injection attacks typically start
with a hacker inputting his or her harmful/malicious code in a specific form field on a
website. A website form, if you dont already know, is something you have definitely used
like when you log into Facebook you are using a form to login, and a form input field can be
any field on a form that asks for your information whether its an email address or a
password, these are all form fields.
For our example of SQL injection, we will use a hypothetical form which many people have
probably dealt with before: the email me my password form, which many websites have in
case one of their users forgets their password.
Subscribe to our newsletter for more free interview questions.
The way a typical email me my password form works is this: it takes the email address as
an input from the user, and then the application does a search in the database for that email
address. If the application does not find anything in the database for that particular email
address, then it simply does not send out an email with a new password to anyone. However,
if the application does successfully find that email address in its database, then it will send
out an email to that email address with a new password, or whatever information is required
to reset the password.
But, since we are talking about SQL injection, what would happen if a hacker was not trying
to input a valid email address, but instead some harmful SQL code that he wants to run on
someone elses database to steal their information or ruin their data? Well, lets explore that
with an example, starting from how a hacker would typically get started in order to figure out
a system works.

Starting the SQL Injection Process

The SQL that would retrieve the email address in the email me my password form would
typically look something like this keep in mind that this SQL really is embedded within a
scripting language like PHP (it depends on what scripting language is being used by the
application):
SELECT data
FROM table
WHERE Emailinput = '$email_input';

This is, of course, a guess at what the SQL being run by the application would look like,
because a hacker would not know this information since he does not have access to the
application code. The $email_input variable is used to hold whatever text the user inputs
into the email address form field.

Step 1: Figure out how the application handles bad inputs


Before a hacker can really start taking advantage of a weak or insecure application, he must
figure out how the application handles a simple bad input first. Think of this initial step as the
hacker feeling out his opponent before he releases the really bad SQL.
So, with that in mind, the first step a hacker would typically take is inputting an email address
with a quote appended to the end into the email form field. We will of course explain why
further down below. But for now, the input from the hacker would look something like this
pay special attention to the fact that there is a quote appended to the end of the email address:
hacker@programmerinterview.com'

If the hacker puts that exact text into the email address form field then there are basically 2
possibilities:

1. The application will first sanitize the input by removing the extra quote at the
end, because we will assume that the application considers email addresses with
quotes as potentially malicious. But, a side note: email addresses can actually contain
quotes according to IETF standards. Sanitizing data is the act of stripping out any
characters that arent needed from the data that is supplied in our case, the email
address. Then, the application may run the sanitized input in the database query, and
search for that particular email address in the database (without the quote of course).

2. The application will not sanitize the input first, and will take the input from the
hacker and immediately run it as part of the SQL. This is what the hacker is hoping
would happen, and we will assume that this is what our hypothetical application is
doing. This is also known as constructing the SQL literally, without sanitizing. What
it means is that the SQL being run by the application would look like this pay extra
attention to the fact that there is now an extra quote at the end of the WHERE
statement in the SQL below:

SELECT data
FROM table
WHERE Emailinput = 'hacker@programmerinterview.com'';

Now, what would happen if the SQL above is executed by the application? Well, the SQL
parser would see that there is an extra quote mark at the end, and it will abort with a syntax
error.

The error response is key, and tells the hacker a lot


But, what will the hacker see on the actual form page when he tries to input this email
address with a quote at the end? Well, it really depends on how the application is set up to
handle errors in the database, but the key here is that the hacker will most likely not receive
an error saying something like This email address is unknown. Please register to create an
account which is what the hacker would see if the application is actually sanitizing the
input. Since we are assuming that the application is not sanitizing its input, the hacker would
most likely see something like Internal error or Database error and now the hacker also
knows that the input to the database is not being sanitized . And if the application is not
sanitizing its input then it means that the database can most probably be exploited, destroyed,
and/or manipulated in some way that could be very bad for the application owner.

Step 2: Run the actual SQL injection attack


Now that the hacker now knows the database is vulnerable he can attack further to get some
really good information. What could our hacker do? Well, if hes been able to successfully
figure out the layout of the table, he could just type this harmful code on the form field
(where the email address would normally go):
Y';
UPDATE table
SET email = 'hacker@ymail.com'
WHERE email = 'joe@ymail.com';

Note that the SQL above is completely SQL compliant and legitimate. You can see that after
the Y there is an extra quote followed by a semicolon, which allows the hacker to close the
statement and then incredibly run another statement of his own!
Then, if this malicious code is run by the application under attack, it would look like this:
SELECT data
FROM table
WHERE Emailinput = 'Y';
UPDATE table
SET email = 'hacker@ymail.com'
WHERE email = 'joe@ymail.com';

Can you see what this code is doing? Well, it is resetting the email address that belongs to
joe@ymail.com to hacker@ymail.com. This means that the hacker is now changing a
users account so that it uses his own email address hacker@ymail.com. This then means
that the hacker can reset the password and have it sent to his own email address! Now, he
also has a login and a password to the application, but it is under someone elses account.

In the example above, we did skip some steps that a hacker would have taken to figure out
the table name and the table layout, because we wanted to keep this article relatively short.
But, the idea is that SQL injection is a real threat, and taking measures to prevent it is
extremely important.
Now, the question is how to prevent SQL injection attacks? Well, read on to the
next page or just click here: SQL Injection Prevention.

In our earlier tutorial on SQL Injection, one way to have prevented the SQL injection attack
was by simply having the user input sanitized which we briefly discussed. Since we are
dealing with email addresses in our example, this means that we should be able to safely
exclude certain characters which dont normally appear in email addresses. Here is a list of
characters that would normally appear in emails, and anything else should not be allowed
inside the database the user should just receive an error saying something like Invalid
email address if he tries to input an email address with any characters other than the ones
below:
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
! $ & * - = ^ ` | ~ # % ' + / ? _ { } @ .

Sanitizing input is not enough to prevent SQL injection


Unfortunately, just sanitizing user inputs is not enough to prevent SQL injection as you will
see in the examples below. So, lets explore some other options and see what works and why
its good to know all the options, so be sure to read everything.
Subscribe to our newsletter for more free interview questions.

What about escaping strings? Shouldnt this remove the


threat of quotes in SQL injection?
In case you forgot what escaping means in the context of programming, basically its just
allowing special characters (like single/double quotes, percent signs, backslashes, etc.) in
strings to be saved so that they remain as part of the string, and are not mis-interpreted as
something else. For example, if we want to include a single quote in a string that gets output
to the browser in PHP (note in the word its we have a single quote that will be output),
then we have to add a backslash to the single quote so that PHP outputs it as a single quote:
echo 'Programmer Interview - It\'s Great!';

So, when this is displayed on a webpage it will look like:


Programmer Interview - It's Great!

This is whats called escaping strings. If we did not escape the quote in our string then it
would not output anything, and would result in a PHP error because the quote is also used to
enclose the characters in an echo statement.

Now, how would escaping the quotes have helped in our previous example? Remember our
hacker is trying to input this harmful/malicious code into the email form field:
Y';
UPDATE table
SET email = 'hacker@ymail.com'
WHERE email = 'joe@ymail.com';

What if we escape the quotes in the string above before we pass the SQL to the database?
Well, that would mean the quotes in the string become a part of the string that is searched for
using the Emailinput field in effect the query is searching for an email address that is equal
to that giant string. In other words, the quotes are part of the string literal, and will not be
interpreted as SQL. In MySQL, we can escape a quote simply by prepending a quote with
another quote basically 2 single quotes will be interpreted as one quote which is what we
do in the example below. So, the actual SQL that will be run looks like this:
SELECT data
FROM table
WHERE Emailinput = Y''; --the quote after the Y is escaped
UPDATE table SET email = ''hacker@ymail.com'' -- escape quotes
WHERE email = ''joe@ymail.com'' ; --and, more quotes escaped

The key in the example above is that the quotes are now being treated as part of a string that
gets compared to a field in the table, and NOT being translated as actual SQL its very
important that you understand the distinction because it is exactly the problem that
escaping quotes solves for us.
If we do not escape quotes, it allows those quotes to become part of the SQL, and basically
allows the hacker to run 2 statements at once which is exactly what is so dangerous. The
2nd statement (the UPDATE table SET email = hacker@ymail.com WHERE email =
joe@ymail.com';) is what really messes things up, because it allows the hacker to change
the email address of an existing account to his own email address. And, that 2nd statement is
only allowed to run because the quotes are not escaped. Escaping a string is also known as
quotesafing, since you are essentially making the SQL query safe for quotes.

Just Escaping Strings Does Not Prevent SQL Injection


Although we went through an example in which escaping the string prevented the SQL
injection attack, just escaping strings is actually not enough protection against SQL injection
attacks. A decent hacker can run another attack, by exploiting the fact that some databases
allow people to escape strings in more than just one way. MySQL actually allows you to
escape quotes in a variety of different ways in fact as you can see below in some
information pulled straight from the MySQL reference pages, you can easily escape quote
characters by preceding them with a backslash a \ :
There are several ways to include quote characters within a
string that goes into a MySQL query:
1.A ' inside a string quoted with ' may be written as ''.
2.A " inside a string quoted with " may be written as "".
3.Precede the quote character by an escape character (\).

Lets say that we choose to escape quotes manually by just adding a single quote every time a
string comes in with a quote. Because, if we have a name field, we want to allow people with
quotes in their name to be able to save their name without any issues for instance, someone
with the name Jack OLeary should be able to be saved in our database without the quote
causing any issues.
So, if we are retrieving someones name from our database, then the SQL may look like this:
SELECT *
FROM customers
WHERE name = 'Jack OLeary';

-- this works great

And this works perfectly fine because the double quotes will be interpreted as a single quote,
and MySQL will search for Jack OLeary (with one quote), and not Jack OLeary (with 2
quotes).
But, lets say a clever hacker realizes that you may be running a MySQL database, and knows
that MySQL also allows you to escape quotes by preceding the quote character with a
backslash so a quote could also be escaped like this: \
So, our clever hacker tries to insert a string like this into the email field on our form:
\'; DROP TABLE users;

But after we do our own manual string escaping (by adding the extra quote), that string turns
into this:
\''; DROP TABLE users; --

So, the SQL that is run will look like this:


SELECT *
FROM customers
WHERE name = '\''; DROP TABLE users; --';

What happens when this SQL is run? Well, the \ gets interpreted by MySQL as a string with
a single quote, meaning that the system will just search for a name with a single quote. The
2nd quote (the one that comes after the \), will allow the hacker to close the first statement,
insert a semicolon, and then run another malicious statement (the DROP TABLE users; code).
The hacker essentially fools the system into NOT escaping one of the extra quotes by taking
advantage of 2 things here:

1. The application developer is trying to escape quotes himself by just appending an


extra quote.
2. MySQL supports escape mechanisms other than just appending a quote. In this
case, the hacker also used the backslash escape mechanism to run his malicious code.

Remember, the quotes are key because it allows the hacker to close one statement and run
any extra statement of his or her choosing.

Lets repeat this again: Just escaping quotes is not enough


to prevent SQL injection
The lesson here is that escaping quotes is unfortunately not enough to prevent all SQL
injection attacks, and also extremely difficult to do correctly on your own. And because of the
latter, many languages that provide database interface libraries have a function that will
handle escaping strings for you. These functions will handle both parsing of the string and
quotesafeing as well so when you use those functions you have a much better chance of
getting things done correctly.
If you are looking for actual examples of those functions, PHP has a function called
mysql_real_escape_string and Perls DBD module has a function called quote. You
absolutely should be using these functions before using form data in your queries.

The best way to prevent SQL Injection Prepared


Statements
But, the best way to prevent SQL injection is to use prepared statements. You can (and
should) read about prepared statements and their role in preventing SQL injection here:
Prepared Statements and SQL Injection
Prepared statements, also known as parameterized statements or parameterized SQL, can be
thought of as a template for SQL statements. Prepared statements allow database engines to
run SQL statements more efficiently because the same (or similar) SQL is used over and over
again well explain more about the details below. The key feature of a prepared statement is
the fact that values can be plugged into the query after the query is prepared, and ready to
be executed. This will make more sense when you see the examples below.

Prepared Statements use Placeholders


Prepared statements use question marks (?), which are placeholders for where actual values
that will be used in the SQL should be plugged in. The placeholders used in prepared
statements are also known as bound parameters, since they are essentially parameters that are
passed to the SQL that bind to the SQL at a later time.
Confused yet? Well, some examples should clear it up its really not difficult to understand
at all.

Examples of Prepared Statements


Below we present some examples of prepared statements in Java, PHP, and Perl. Here we are
using the interface libraries that each language provides to communicate with different
database environments (like MySQL, Oracle, etc). As you may already know, Java uses a
library known as JDBC, PHP uses something called PDO (PHP Data Objects), and Perl uses
something called the Perl DBI (Perl Database Interface)

Example of a prepared statement in Java using JDBC:


java.sql.PreparedStatement stmt =
connection.prepareStatement(
"SELECT * FROM table WHERE EMAIL = ?");
/* The statement below sets "?" to an actual value that
is stored in the email variable, we are also assuming
that the email variable is set beforehand: */
stmt.setString(1, email);
stmt.executeQuery();

Example of a prepared statement in PHP using PDO:


$stmt = $dbh->prepare("SELECT * FROM
table WHERE EMAIL = ? ");
/* The statement below sets "?" to an actual value that
is stored in the email variable, we are also assuming
that the $email variable is set beforehand: */
$stmt->execute($email);

Example of a prepared statement in Perl using Perl DBI:


my $stmt = $dbh->prepare('SELECT * FROM
table WHERE EMAIL = ?');
/* The statement below sets "?" to an actual value that
is stored in the email variable, we are also assuming
that the email variable is set beforehand: */
$stmt->execute($email);

Looking at the examples above, you can see that even though the syntax details are different
for each language, they are all fundamentally the same because they all use a ? as a
placeholder for the value that will be passed in later. And they all prepare the SQL first and
execute later, which is of course the whole point behind prepared statements. A good way to
think of a prepared statement is as a template for SQL because of the fact that its not a
complete SQL statement since it does not have the values it needs in the placeholder areas.

What exactly happens when SQL is prepared?


Prepared SQL is created by calling the respective prepare method in each language, as you
can see in the examples above. The prepared SQL template is sent to the DBMS (whether its
MySQL, DB2, or whatever) with the placeholder values (the ?) left blank. Then, the DBMS
will parse, compile, and perform query optimization on the template. After that, the DBMS
will store the result, but it can not execute the result because it, of course, does not have any

values to execute with since there is no data in the placeholders/parameters. The SQL is only
executed once the respective execute function is called and data is passed in for the
parameters.

What are the advantages of using prepared statements?


Prepared statements provide 2 primary benefits. The first is that they provide better
performance. Even though a prepared statement can be executed many times, it is is
compiled and optimized only once by the database engine. Because of the fact that a prepared
statement does not have to be compiled and optimized each and every time the values in the
query change, it offers a distinct performance advantage. But, keep in mind that not all query
optimization can occur when a prepared statement is compiled. This is because the best query
plan may also depend on the specific values of the parameters being passed in. The best query
plan may also change over time, because of the fact that the database tables and indices also
change over time.

Why are prepared statements so effective against SQL


injection?
The second advantage of using prepared statements is that they are the best solution to
preventing SQL injection attacks. If you are not familiar with SQL injection, its highly
recommended that you read our article on SQL injection every programmer should know
what SQL injection is. A short, non-academic description of SQL injection is this: any time
an application runs SQL based on some user input through a web form, then a hacker could
potentially pass in some input with the intent of having his input run as part of your SQL, and
either steal or corrupt your users data.
Now, back to our discussion: the reason that prepared statements help so much in preventing
SQL injection is because of the fact that the values that will be inserted into a SQL query are
sent to the SQL server after the actual query is sent to the server. In other words, the data
input by a potential hacker is sent separately from the prepared query statement. This means
that there is absolutely no way that the data input by a hacker can be interpreted as SQL, and
theres no way that the hacker could run his own SQL on your application. Any input that
comes in is only interpreted as data, and can not be interpreted as part of your own
applications SQL code which is exactly why prepared statements prevent SQL injection
attacks.

Anda mungkin juga menyukai