Forms of SQL
injection
vulnerabilities
Incorrectly filtered
escape characters
This
form of SQL
injection occurs
when user input is
not filtered for
escape characters
and is then passed
into a SQL
statement. This
results in the
potential
manipulation of the
statements performed
on the database by
the end user of the
application.
The
following line of
code illustrates
this vulnerability:
statement := "SELECT * FROM users WHERE name = '" + userName + "';"
If
the "userName"
variable is crafted
in a specific way by
a malicious user,
the SQL statement
may do more than the
code author
intended. For
example, setting the
"userName" variable
as
a' or 't'='t
renders this SQL
statement by the
parent language:
SELECT * FROM users WHERE name = 'a' or 't'='t';
If
this code were to be
used in an
authentication
procedure then this
example could be
used to force the
selection of a valid
username because the
evaluation of
't'='t' is always
true.
Theoretically any
valid SQL command
may be injected via
this method,
including the
execution of
multiple statements.
The following value
of "userName" in the
above statement
would cause the
deletion of the
"users" table as
well as the
selection of all
data from the "data"
table:
a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '%
This
input renders the
final SQL statement
as follows:
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM data WHERE name LIKE '%';
Incorrect type
handling
This
form of SQL
injection occurs
when a user supplied
field is not
strongly typed or is
not checked for type
constraints. This
could take place
when a numeric field
is to be used in a
SQL statement, but
the programmer makes
no checks to
validate that the
user supplied input
is numeric. For
example:
statement := "SELECT * FROM data WHERE id = " + a_variable + ";"
It
is clear from this
statement that the
author intended
a_variable to be a
number correlating
to the "id" field.
However, if it is in
fact a string then
the end user may
manipulate the
statement as they
choose, thereby
bypassing the need
for escape
characters. For
example, setting
a_variable to
1;DROP TABLE users
will
delete the "users"
table from the
database as the
rendered SQL would
be rendered as
follows:
SELECT * FROM data WHERE id = 1;DROP TABLE users;
Vulnerabilities
inside the database
server
Sometimes
vulnerabilities can
exist within the
database server
software itself, as
was the case with
the MySQL server's
real_escape_chars()
functions recently.
Securing
applications against
SQL injection
Application
remediation
SQL
injection is easy to
work around in most
programming
languages that
target web
applications or
offer functionality.
In Perl DBI, the
DBI::quote
method escapes
special characters
(assuming the
variable
$sql
holds a reference to
a DBI object):
$query = $sql->prepare
(
"select * from users where name = "
.
$sql->quote($user_name)
);
However, this is
generally not the
best way to approach
the issue. DBI
allows the use of
placeholders, which
let you bind data to
a statement
separately to
defining the SQL
statement. For
databases that do
not natively support
placeholders, DBI
emulates them by
automatically
applying the
DBI::quote
function to the
values. Many
databases do support
binding values
separately via their
APIs; DBI will use
the native
placeholder support
in this case. For
example:
$query = $sql->prepare("select * from users where name = ?");
$query->execute($user_name);
The
advantage is that
you do not have to
remember to apply
DBI::quote
to every value. It
is either bound
separately, or
quoted
appropriately,
depending on the
support offered by
the particular DBMS
you are using. You
then avoid the basic
issue of SQL
injection where
values are
interpreted as SQL.
For
databases that
support placeholders
natively, there are
often significant
performance
advantages to using
placeholders, as the
database can cache
the compiled
representation of a
statement and reuse
it between
executions with
different bound
values. Placeholders
are also sometimes
referred to as 'bind
variables'.
In
PHP, there are
different built-in
functions to use for
different DBMSes for
escaping values
suitable for
embedding in literal
SQL statements. For
MySQL, the
equivalent is the
built-in function
mysql_real_escape_string:
$query_result = mysql_query
(
"select * from users where name = '"
.
mysql_real_escape_string($user_name)
.
"'"
);
The
native interface to
a particular DBMS
may also offer a
method of binding
placeholders
separately, for
example,
mysql_stmt_bind_param,
or oci_bind_by_name.
Alternatively, a
database abstraction
library can be used
to emulate
placeholders in a
similar way to
Perl's DBI. One
example out of
several available
libraries is ADOdb.
In
the Java programming
language, the
equivalent is the
PreparedStatement
class.
Instead of
Connection con = (acquire Connection)
Statement stmt = con.createStatement();
ResultSet rset = stmt.executeQuery("SELECT * FROM users WHERE name = '" + userName + "';");
use
the following
Connection con = (acquire Connection)
PreparedStatement pstmt = con.prepareStatement("SELECT * FROM users WHERE name = ?");
pstmt.setString(1, userName);
ResultSet rset = pstmt.executeQuery();
In
the .NET or Mono
programming language
"C#", the equivalent
are the ADO.NET
SqlCommand (for
Microsoft SQL
Server) or
OracleCommand (for
Oracle's database
server) objects. The
example below shows
how to prevent
injection attacks
using the SqlCommand
object. The code for
other ADO.NET
providers is very
similar, but may
vary slightly
depending on the
specific
implementation by
that provider
vendor.
Instead of
using( SqlConnection con = (acquire connection) ) {
con.Open();
using( SqlCommand cmd = new SqlCommand("SELECT * FROM users WHERE name = '" + userName + "'", con) ) {
using( SqlDataReader rdr = cmd.ExecuteReader() ){
...
}
}
}
use
the following
using( SqlConnection con = (acquire connection) ) {
con.Open();
using( SqlCommand cmd = new SqlCommand("SELECT * FROM users WHERE name = @userName", con) ) {
cmd.Parameters.Add("@userName", userName);
using( SqlDataReader rdr = cmd.ExecuteReader() ){
...
}
}
}
|