How do prepared statements help us prevent SQL injection attacks?
Wikipedia says:
Prepared statements are resilient against SQL injection, because
parameter values, which are transmitted later using a different
protocol, need not be correctly escaped. If the original statement
template is not derived from external input, SQL injection cannot
occur.
I cannot see the reason very well. What would be a simple explanation in an easy English and some examples?
The idea is very simple - the query and the data are sent to the database server separately.
That's all.
The root of the SQL injection problem is in the mixing of the code and the data.
In fact, our SQL query is a legitimate program.
And we are creating such a program dynamically, adding some data on the fly. Thus, the data may interfere with the program code and even alter it, as every SQL injection example shows it (all examples in PHP/Mysql):
$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";
will produce a regular query
SELECT * FROM users where id=1
while this code
$spoiled_data = "1; DROP TABLE users;"
$query = "SELECT * FROM users where id=$spoiled_data";
will produce a malicious sequence
SELECT * FROM users where id=1; DROP TABLE users;
It works because we are adding the data directly to the program body and it becomes a part of the program, so the data may alter the program, and depending on the data passed, we will either have a regular output or a table users deleted.
While in case of prepared statements we don't alter our program, it remains intact
That's the point.
We are sending a program to the server first
$db->prepare("SELECT * FROM users where id=?");
where the data is substituted by some variable called a parameter or a placeholder.
Note that exactly the same query is sent to the server, without any data in it! And then we're sending the data with the second request, essentially separated from the query itself:
$db->execute($data);
so it can't alter our program and do any harm.
Quite simple - isn't it?
The only thing I have to add that always omitted in the every manual:
Prepared statements can protect only data literals, but cannot be used with any other query part.
So, once we have to add, say, a dynamical identifier - a field name, for example - prepared statements can't help us. I've explained the matter recently, so I won't repeat myself.
Here is an SQL statement for setting up an example:
CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);
INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);
The Inject class is vulnerable to SQL injection. The query is dynamically pasted together with user input. The intent of the query was to show information about Bob. Either salary or bonus, based on user input. But the malicious user manipulates the input corrupting the query by tacking on the equivalent of an 'or true' to the where clause so that everything is returned, including the information about Aaron which was supposed to be hidden.
import java.sql.*;
public class Inject {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
System.out.println(sql);
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
Running this, the first case is with normal usage, and the second with the malicious injection:
c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50
c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0
You should not build your SQL statements with string concatenation of user input. Not only is it vulnerable to injection, but it has caching implications on the server as well (the statement changes, so less likely to get a SQL statement cache hit whereas the bind example is always running the same statement).
Here is an example of Binding to avoid this kind of injection:
import java.sql.*;
public class Bind {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
Connection conn = DriverManager.getConnection(url);
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
System.out.println(sql);
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, args[0]);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
Running this with the same input as the previous example shows the malicious code does not work because there is no paymentType matching that string:
c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50
c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
Basically, with prepared statements the data coming in from a potential hacker is treated as data - and there's no way it can be intermixed with your application SQL and/or be interpreted as SQL (which can happen when data passed in is placed directly into your application SQL).
This is because prepared statements "prepare" the SQL query first to find an efficient query plan, and send the actual values that presumably come in from a form later - at that time the query is actually executed.
More great info here:
Prepared statements and SQL Injection
I read through the answers and still felt the need to stress the key point which illuminates the essence of Prepared Statements. Consider two ways to query one's database where user input is involved:
Naive Approach
One concatenates user input with some partial SQL string to generate a SQL statement. In this case the user can embed malicious SQL commands, which will then be sent to the database for execution.
String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"
For example, malicious user input can lead to SQLString being equal to "SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'
Due to the malicious user, SQLString contains 2 statements, where the 2nd one ("DROP TABLE CUSTOMERS") will cause harm.
Prepared Statements
In this case, due to the separation of the query & data, the user input is never treated as a SQL statement, and thus is never executed. It is for this reason, that any malicious SQL code injected would cause no harm. So the "DROP TABLE CUSTOMERS" would never be executed in the case above.
In a nutshell, with prepared statements malicious code introduced via user input will not be executed!
When you create and send a prepared statement to the DBMS, it's stored as the SQL query for execution.
You later bind your data to the query such that the DBMS uses that data as the query parameters for execution (parameterization). The DBMS doesn't use the data you bind as a supplemental to the already compiled SQL query; it's simply the data.
This means it's fundamentally impossible to perform SQL injection using prepared statements. The very nature of prepared statements and their relationship with the DBMS prevents this.
In SQL Server, using a prepared statement is definitely injection-proof because the input parameters don't form the query. It means that the executed query is not a dynamic query.
Example of an SQL injection vulnerable statement.
string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";
Now if the value in the inoutusername variable is something like a' or 1=1 --, this query now becomes:
select * from table where username='a' or 1=1 -- and password=asda
And the rest is commented after --, so it never gets executed and bypassed as using the prepared statement example as below.
Sqlcommand command = new sqlcommand("select * from table where username = #userinput and password=#pass");
command.Parameters.Add(new SqlParameter("#userinput", 100));
command.Parameters.Add(new SqlParameter("#pass", 100));
command.prepare();
So in effect you cannot send another parameter in, thus avoiding SQL injection...
The key phrase is need not be correctly escaped. That means that you don't need to worry about people trying to throw in dashes, apostrophes, quotes, etc...
It is all handled for you.
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");
Let’s assume you have that in a Servlet you right. If a malevolent person passed a bad value for 'filter' you might hack your database.
The simple example:
"select * from myTable where name = " + condition;
And if user input is:
'123'; delete from myTable; commit;
The query will be executed like this:
select * from myTable where name = '123'; delete from myTable; commit;
Root Cause #1 - The Delimiter Problem
Sql injection is possible because we use quotation marks to delimit strings and also to be parts of strings, making it impossible to interpret them sometimes. If we had delimiters that could not be used in string data, sql injection never would have happened. Solving the delimiter problem eliminates the sql injection problem. Structure queries do that.
Root Cause #2 - Human Nature, People are Crafty and Some Crafty People Are Malicious And All People Make Mistakes
The other root cause of sql injection is human nature. People, including programmers, make mistakes. When you make a mistake on a structured query, it does not make your system vulnerable to sql injection. If you are not using structured queries, mistakes can generate sql injection vulnerability.
How Structured Queries Resolve the Root Causes of SQL Injection
Structured Queries Solve The Delimiter Problem, by by putting sql commands in one statement and putting the data in a separate programming statement. Programming statements create the separation needed.
Structured queries help prevent human error from creating critical security holes.
With regard to humans making mistakes, sql injection cannot happen when structure queries are used. There are ways of preventing sql injection that don't involve structured queries, but normal human error in that approaches usually leads to at least some exposure to sql injection. Structured Queries are fail safe from sql injection. You can make all the mistakes in the world, almost, with structured queries, same as any other programming, but none that you can make can be turned into a ssstem taken over by sql injection. That is why people like to say this is the right way to prevent sql injection.
So, there you have it, the causes of sql injection and the nature structured queries that makes them impossible when they are used.
Related
I have gone through various document (SO post as well) about how exactly Prepared statement of PDO protect user from SQL injection.
Although,I understand it protect user because in prepared statement,user record is directly not executing on server insted we are sending positional / named parameter ( ? / :name) and then we send actual data in execute statement, and because of that it saves us from SQL Injection.
Well, Now if I have below code for SQL :
$query = "select * from user where id = $user_input_id";
and user input id = 1
So query will be something like :
$query = "select * from user where id = 1";
This is perfect till now. But if user entre $id = "1; DROP TABLE users;" so query will be something like :
$query = "SELECT * FROM users where id=$id";
and hence ,it will execute
$query = "SELECT * FROM users where id=1; DROP TABLE users;";
It works and out user table will drop because this query directly execute:
Well,I have read that prepared statement can save user from this :
and prepared statement working like :
$data = "1; DROP TABLE users;"
$db->prepare("SELECT * FROM users where id=?");
$db->execute($data);
In execute statement as well,record with Drop table is passing,so how exactly it won't execute drop table statament ? execute also performing some part on server right ?
Anyone can please explain how exactly prepared statement here save user from SQL injection ?
Thanks
Without explicitly setting a type (see PDOStatement::bindValue() for an example), it will treat the passed value as a string, so it will do this effectively:
SELECT * FROM users where id='1; DROP TABLE users;'
Btw, this would actually happen if you're using emulated prepared statements (PDO::ATTR_EMULATE_PREPARES); without this, it will send the parametrised query first followed by the actual data.
That is why you can additionally set the type of binded data to the type you need.
$stm->bindParam(":id", $id, PDO:PARAM_INT)
Additionally, PDO does some escaping of the data, and the string you provided will not break the query at ;, but will be inserted as plain string in the db.
SQL injection is an attack against the SQL parsing step, not the statement execution step. In this, it has similarities to other parse attacks such as cross site scripting and XML injection attacks.
SQL injection works because the common (broken) technique of creating SQL statements by using string concatenation operators to combine both code and (untrusted) data in a single string allows for the possibility of a specially crafted string to violate the statement data protocol (typically by breaking out of a data context using string delimiters embedded in data), and allowing the attacker to manipulate the SQL parser into executing different code to that originally intended.
When one uses a prepared statement, one is telling the parser 'treat the statement purely as trusted code, and provide some slots into which I will insert the data for execution'.
When you drop the string '1; drop table users' into the data slot you created using the '?' placeholder, that string is not processed by the parser, and hence it has no opportunity to influence the parsing of the string : you made it impossible for the contents of the string to break out of a data context.
Using your example, the database will execute the equivalent statement to :
SELECT * FROM users where id="1; drop table users;"
This is a perfectly valid select statement, which may or may not return rows depending on the data in your tables, but which is almost certainly not going to work properly.
Nevertheless, the approach bypassed the attempt at SQL injection.
Be aware : using prepared statements is the ONLY generalised way to avoid SQL injection attacks. In general, attempts to filter untrusted input data are broken.
I have read somewhere here that using prepared statements in PDO makes your app only immune to first order SQL injections, but not totally immune to second order injections.
My question is: if we used prepared statements in all queries inlcuding SELECT queries and not only in INSERT query, then how can a second order sql injection be possible?
For example in the following queries there is no chance for a 2nd order injection:
write:
INSERT INTO posts (userID,text,date) VALUES(?,?,?)
read:
SELECT * FROM posts WEHRE userID=?
delete:
DELETE FROM posts WHERE userID=?
What you have read is a plain rubbish. Someone who wrote it just have no clue.
You should use prepared statements not for the query but for the data. Every time you have to add a variable into query, you have to make it via placeholder only. So, your query separation theory makes no sense: it doesn't matter if it SELECT or ALTER or GRANT or whatever. The only thing that matters - if any variable goes into query or not.
Since most people sermonize “the user is evil” and “don’t trust user input”, one may get the impression that once the data is in the database it’s ‘trusted’.
But SQL injections is not about trusted and untrusted data. SQL injection is the failure of ensuring that an SQL statement is interpreted as intended.
And this is where prepared statements/parameterization comes in play as it’s a technique to ensure that the parameters are interpreted as intended, i. e., as data and not as SQL code. And this should be applied to any data, regardless of its origin or whether it’s seen as ‘trusted’ or ‘untrusted’, simply to ensure the data is interpreted as intended.
There's a lot of warnings about SQL injections here on SO, but no one I've found doesn't really answer, how does it happen? In this question, I'm assuming it's MySQL and PHP.
The basic mysql_ doesn't accept a second query inside a query, right?
So, basically, this
$unsafe = "');DROP TABLE table;--";
mysqli_query($con,"INSERT INTO table (Column) VALUES ('$unsafe'");
doesn't actually do anything harmful? Correct me on this.
I've no experience working with mysqli_, so I'll skip to PDO, and "Prepared statements".
When I started working with PDO, I had a lack of information on it, and basically used it like this, thinking it's safe.
$stm = $pdo->prepare("INSERT INTO table (Column) VALUES ('$unsafe');
$stm->execute();
However, same thing with PDO as with mysql_. It doesn't support multiple queries out of the box, right? Again, correct me on this.
Then, this is consired safe, if I'm not wrong?
$stm = $pdo->prepare("INSERT INTO table (Column) VALUES (?);
$stm->execute(array($unsafe);
How does a malicious user with no access to the database inject malicious data, if multiple queries aren't even supported?
Or are they?
How does a malicious user with no access to the database inject malicious data, if multiple queries aren't even supported?
"SQL injection" is not equal to "second query".
Or are they?
Surely they are.
Second query is just an example. While it can be any valid SQL statement. SQl injection is an exploit of improperly formatted query. If a developer don't format SQL properly, there is a possibility to break from limits of literal and add code to the SQL body.
Is an SQL injection actually possible by adding a second query?
Yes, depends on the API you are using.
Two queries with mysql + php is a fallacy
Source: http://xkcd.com/327/
This will not work with mysql and php without deliberate steps to make it possible, since the normal query function will only execute the first query.
That doesn't mean it's not possible - only that it should be very obvious when it is.
SQL injection is very real
But the above means almost nothing in terms of sql injection. There is a huge, huge amount of information out there about sql injection including a large number of questions here on stack overflow. Taking the example in the question, this is an equivalent attack which would work:
$id = "123 OR 1 = 1 --";
mysqli_query($con,"DELETE FROM table WHERE id = $id LIMIT 1");
i.e. finding an interface to delete my own, e.g., comment, if the id is not escaped it would be trivial to delete all comments. But this example is just the very tip of an iceberg.
Executing arbitrary sql statements are exploitable
This code in the question:
$stm = $pdo->prepare("INSERT INTO table (Column) VALUES ('$unsafe')");
$stm->execute();
Has none of the benefits of using PDO - i.e. any exploit (of the truly massive number) that would work with the mysql/mysqli driver (used naively) will work with pdo used in this way.
Parametrized queries protect against sql injection
Using PDO with prepared statements with parameters escapes values appropriately preventing sql injection attacks, so yes this is safe from injection:
$stm = $pdo->prepare("INSERT INTO table (Column) VALUES (?)");
$stm->execute(array($unsafe));
How does a malicious user with no access to the database inject malicious data
Simply by finding a way to execute sql that either does what they want to do, or gives them the information to do it a different way.
For example:
function login() {
$username = "irrelevant' OR is_admin = 1 --";
$password = hash('irrelevant');
$query = "SELECT id from users where username = '$username' AND password = '$password'";
...
}
How did malicious user get access to the admin functionality on a system with no concern for injection? Very easily.
For general information about injection see the previous references.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
What is SQL injection?
I see a lot of php code floating around on stackoverflow and (too) little escaping of strings.
Can anyone
Explain what SQL injection is;
Explain what it can do to your server, data and code;
Give an example how to perform an SQL-injection
Give php sample code how to protect against SQL-injection
An SQL injection is a maliciously formed SQL query used to "confuse" an SQL database into giving something it shouldn't. For instance, consider the following query
"SELECT * FROM `users` WHERE `username` = '$name'";
In a normal case, this will work. If we submit 'Jack' to this, it will return all users named Jack. However, if a user enters, say "' OR 1=1", the resulting query would be
"SELECT * FROM `users` WHERE `username` = '' OR 1=1";
Since 1 always equals 1, and the combinating clause is OR, this will return true on every row, which will in turn display EVERY row to the malicious user. Using this technique, someone can view your entire database. Also consider if someone submits something like "'; DROP TABLE users";--, which results in
"SELECT * FROM `users` WHERE `username` = ''; DROP TABLE `users`";--";
Which is two queries, one which will do nothing, the second which will delete the ENTIRE users database, resulting in the loss of your data.
The best method to prevent SQL injections is to use prepared statements. With these, you send a query to the SQL database that says something like
"SELECT * FROM `users` WHERE `username` = '?'";
This lets the database know the format of the query (WHERE username equals some value), so there is no confusion when given a plain text query. Then the database knows to expect one value, and where to put it. Then you pass that value to the database which it can use to search. This is also better as the database can optimize the query for faster searching.
Read up on prepared statements, which will explain this in more detail.
I cannot resist aswell.
SQL Injection is "a code injection technique that exploits a security vulnerability occurring in the database layer of an application". In other words it's SQL code injected in as user input inside a query.
SQL Injections can manipulate data (delete, update, add ecc...) and corrupt or delete tables of the database. I'm not aware of SQL Injections manipulating scripts though.
Let's say in your PHP script you are expecting (as user input) a username and a password from the login form that are later used inside a query such as:
SELECT Id FROM Users WHERE Name = $name AND Password = $password;
The user can insert inside $name and as $password whatever he likes (for example trough an <input>). Let's imagine he adds a name such as "1 OR 1 = 1; --", the query will now look like:
SELECT Id FROM Users WHERE Name = 1 OR 1 = 1; -- AND Password = $password;
and then, after the ; I could add another query or make the script think that the username and the password actually exists.
Notice that -- AND Password = $password; is a SQL comment and will therefore be ignored.
If you are using PHP < 5 then you should look for mysql_real_escape_string() and use it to escape user inputs before embedding it inside a query.
If you are using PHP5+ you should use PDO or the mysqli extension which can prevent this problem via prepared statements.
I cannot resist posting this.
1- Sql Injection is explained better in one cartoon, than most other documents.
2- Mostly it does not do much to the server, but only to the underlying data. Consequence include delete, insert , select records, drop, create tables. (based on permissions etc..)
3- Examples.
4- Sorry I do not know PHP. But as long as you can abstract your DB layer from your View, you should be fine.
There's a lot of information out there (and elsewhere in here) about this subject, so do not take this answer as a complete list by any means and continue to research on your own...
Explain what SQL injection is;
Explain what it can do to your server, data and code;
Give an example how to perform an SQL-injection
Give php sample code how to protect against SQL-injection
SQL injection is where an attacker discovers that an input value supplied to your application is being sent directly to a database and realizes that they can craft that input to be a custom SQL command. It could be something as simple as entering a special character (such as %) into a text field and receiving a strange response.
It can do anything your database allows that command to do. For example, if your web application has DB owner permissions for the application's database then an attack can potentially drop tables or even drop the whole database. Or, with even normal application permissions, the attack can over-write data or read sensitive data (such as plain text passwords if you have those).
For example, if an application has a text field where you enter a username. If that field is open to SQL injection, an attacker can enter something like: MyName';DROP TABLE Users;-- In this example, the attack manually finishes the query with the closing single quote and semi-colon, then adds another query, then comments out anything afterward. If not protected against this, the database may run both queries.
This one I don't know updated enough information, but there's lots out there :)
Historically, I've always used
mysql_real_escape_string()
for all input derived from users that ends up touching the database.
Now that I've completely converted over to MySQLi and I'm using prepared queries with bound parameters, have I effectively eliminated the possibility of SQL injection attacks?
Am I correct in saying I no longer need
mysql_real_escape_string()?
This is my understanding and the basis of a project of mine:
http://sourceforge.net/projects/mysqldoneright/files/Base/MysqlDoneRight-0.23.tar.gz/download
This is not something I want to get wrong though as now that I've released it, it could affect others as well.
All user provided input will now end up in bind_parms.
The queries provided in the prepare phase are static.
Yes. Using the prepared query will escape parameters.
It's not so simple. You can use bound parameters instead of interpolating application variables into SQL expressions in place of literal values only:
$sql = "SELECT * FROM MyTable WHERE id = ".$_GET["id"]; // not safe
$sql = "SELECT * FROM MyTable WHERE id = ?"; // safe
But what if you need to make part of the query dynamic besides a literal value?
$sql = "SELECT * FROM MyTable ORDER BY ".$_GET["sortcolumn"]; // not safe
$sql = "SELECT * FROM MyTable ORDER BY ?"; // doesn't work!
The parameter will always be interpreted as a value, not a column identifier. You can run a query with ORDER BY 'score', which is different from ORDER BY score, and using a parameter will be interpreted as the former -- a constant string 'score', not the value in the column named score.
So there are lots of cases where you have to use dynamic SQL and interpolate application variables into the query to get the results you want. In those cases, query parameters can't help you. You still have to be vigilant and code defensively to prevent SQL injection flaws.
No framework or data-access library can do this work for you. You can always construct a SQL query string that contains a SQL injection flaw, and you do this before the data-access library sees the SQL query. So how is it supposed to know what's intentional and what's a flaw?
Here are the methods to achieve secure SQL queries:
Filter input. Trace any variable data that gets inserted into your SQL queries. Use input filters to strip out illegal characters. For instance, if you expect an integer, make sure the input is constrained to be an integer.
Escape output. Output in this context can be the SQL query which you send to the database server. You know you can use SQL query parameters for values, but what about a column name? You need an escaping/quoting function for identifiers, just like the old mysql_real_escape_string() is for string values.
Code reviews. Get someone to be a second pair of eyes and go over your SQL code, to help you spot places where you neglected to use the above two techniques.
When you bind parameters to a prepared statement, it escapes the data automatically, so you shouldn't escape it before you send it through. Double escaping is usually a bad thing. At the very least, it produces ugly results with extra escaped characters later on.