I've come os far as to gaining basic understanding of prepared statements and I get that they prevent SQL-injection attacks. But I have yet to understand WHY they protect against said attacks. I know there are similar questions asked but I didn't find the answers entirely satisfying.
Example - very unsafe code
So here we have the most basic way to communicate with our database:
$query = "SELECT * FROM users where id=$username";
Without any protection, a user can input malicious code and thus "trick" the database engine to execute a devastating query:
$username = "1; DROP TABLE users;"
SELECT * FROM users where id=1; DROP TABLE users;
What I don't understand is how a prepared statement manages to "filter out" such data. What is the mechanic behind it that does NOT lure the database to generate such a SQL-query as shown above? Is just as simple as escaping certain characters, like semicolon in the example above, or is it more complicated?
If I was to do the exact injection attack as in the example, but running it through a prepared statement, what kind of command string would reach the database engine?
Prepared statements don't just add in the text, they send it as data, and let the database process it separately. Because in reality the database doesn't actually use the SQL statements, it uses "compiled" versions of them.
Not quite sure I was clear, but it lies in how the query is sent to the database.
Prepared statements are usually built to use parameter binding. It's really the parameter binding that insulates against these kinds of attacks. You can use parameter binding without using prepared statements.
The second level of protection that prepared statements offer is that each is a single statement (so the use of ; to create two statements out of one won't work).
As a general rule, in order to be safe from injection attacks, the prepared statement must be prepared from data that is not derived from any external input.
Basically, if you use standard untyped parameter binding, you will get
SELECT * FROM users where id='1; DROP TABLE users;'
Which will error out on the Database, but will do no harm.
Please understand, that this is not the sam thing as running
SELECT * FROM users where id='$username'
with a suitably escaped $username - it happens on a lower layer of your DB access stack.
Related
I am learning mysql now and one of the subjects it touches is the security issue when dealing with user input - one concern is the injection attack. I tried to duplicate the attack the book demonstrated like add a query $query = "select * from temp_table; drop table temp_table, which I used mysqli_query($connection,$query). Nothing happen. I changed to use mysqli_multi_query() and found it executed both statements. Finally I found that mysqli_query only runs one query each time.
my question is, if I use mysqli_query, theoretically speaking, the system shouldn't be worried on additional statement injection attack? Or, there is still any other way that the users can run additional statement even the server is using mysqli_query?
It's true that the basic mysqli_query() will only run one statement. So you don't have to worry that an SQL injection attack will trick your application into running multiple statements.
But one statement can include a subquery, or a SELECT... UNION SELECT....
One statement can read data it isn't intended to read. Or cause a huge sort that is intended to overwhelm your server as a denial-of-service attack.
Or it can simply be an error, not a malicious attack at all.
SELECT * FROM Users WHERE last_name = 'O'Reilly'; -- woops!
The solutions to SQL injection are pretty simple, and easy to follow. I don't understand why so many developers look for excuses not to write safe code.
Everyone knows that PDO prepare statement helps us prevent SQL injection attack. How about this:
function userQuery($username){
$mysqli->multi_query("
PREPARE stmt1 FROM 'SELECT * FROM user WHERE username=?';
SET #a = '$username';
EXECUTE stmt1 USING #a
");
}
userQuery('Kelvin');
Is this as safe as mysqli or PDO prepare statement?
I'm asking this question because I found these sentences on wiki:
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.
They mention about parameters are transmitted later with different protocol. And I don't really understand
this.
How can parameters are transmitted later with different protocol prevent injection attack?
[I assume that you meant $mysqli is mysqli connection.]
Although the execution of stmt1 (the last of your three queries) is safe, the multi-query function you wrote is very unsafe. Running something like
userQuery("'; delete from user; select * from user where username='");
will actually delete all users from your user table. Assuming $username represents raw user input without proper escaping, the consequences can be catastrophic.
You could possibly improve the above and do the escaping on your own using mysqli::real_escape_string, but there are many more sophisticated ways to do hacks like one above. Prepared statements are all in all a better solution.
I'm using PHP PDO for my queries, everywhere, but I read that in very rare cases there could still be "second order injections" where an unsafe variable is stored then executed when used in another statement.
Will prepared statements still protect against this? As long as I make sure I always use them? Or do I have to take more precautions? Am I still vulnerable to XSS attacks?
I also have a couple more questions, just out of curiosity, if you all don't mind:
Is it possible to have an SQL Injection with only alphanumeric characters, spaces, and one dash? Like select * from something where name='$some_variable'. All the examples I've seen seem to require other characters like semicolons, quotes, or double dashes.
I've read many SQL examples where the unsafe variable could be set to form another statement, eg
$foo = "foo'); INSERT INTO users (name) VALUES ('hi";
$bar = ("INSERT INTO users (name) VALUES ('$foo')");
But I just tested and mysql_query doesn't even allow multiple statements. I know you can still have injections within 1 statement, but can I confirm that you won't have problems with multiple statements in PHP?
Not to beat a dead (or is it a very alive?) horse, but...
Injection can only happen when data is read by the SQL engine as commands. In a very simple case, if you allow unescaped " characters in your data, and your data is encapsulated by " characters in SQL, they you have enabled an SQL injection attack.
The key to preventing any SQL injection is to properly validate and escape incoming data EVERY time, at the time it goes into the SQL statement. An easy way to do this is to just use prepared statements, which take care of it for you, allowing you to safely pass parameters to an SQL statement.
Each database library has it's own way of escaping or using prepared statements. In MySQL and PHP, you have mysqli_real_escape_string(), which should be used EVERY TIME PERIOD, when you are using the mysqli library.
The PDO library has it's own way, but if I recall correctly, prepared statements were a big part of PDO -- use them 100% of the time, and you will be OK in that regard.
To prevent agains XSS attacks, use HTML Purifier, and never strip_tags(), see links below for more info, PDO prepared statements should be fine for SQL Injection prevention:
http://www.reddit.com/r/PHP/comments/nj5t0/what_everyone_should_know_about_strip_tags/
http://htmlpurifier.org/
I know that this question may be closed by some of you, but my question came up from you and your answers. I am reading the past two hours questions and answers for SQL Injections and how to protect your database. The same comes to the huge amount of webpages and tutorials I saw.
I found out that half of the people claim that prepare statements do secure your db, and the other 50 claim that it is not.
On the other hand, I read that mysql_real_escape_string does the job, and other people saying that it is not.
My question is who to believe ?
In addition, is this a proper prepare statement?
$stmt = $dbh->prepare("SELECT phpro_user_id, phpro_username, phpro_password FROM phpro_users
WHERE phpro_username = :phpro_username AND phpro_password = :phpro_password");
/*** bind the parameters ***/
$stmt->bindParam(':phpro_username', $phpro_username, PDO::PARAM_STR);
$stmt->bindParam(':phpro_password', $phpro_password, PDO::PARAM_STR, 40);
/*** execute the prepared statement ***/
$stmt->execute();
Both. Prepared statements will protect you against SQL injections if, and only if, you use them in a correct manner. Just' using' prepared statements won't help if you're still interpolating variables for table/column names for example.
$stmt = "SELECT * FROM $table WHERE $column = ?"; //not good...
Prepared statements don't. Bound parameters secure the statement (not the database as a whole) so long as all your untrusted data is passed via a parameter rather than being interpolated into the statement. When people use prepared statements, they almost always use bound parameters too, so the two names are often conflated.
Prepare statement
Run statement with variables as additional arguments
mysql_real_escape_string almost always does the job, but since it adds additional steps to the process, it is more prone to human error.
Escape each variable
Concatenate variables into SQL statement
Run statement
This is a good discussion. Your question assumes there is one technique that will "secure your database". In fact, there is no single technique that is best for all cases. So you need to learn to use multiple solutions in different situations.
Escaping literal values
Parameter placeholders in prepared queries
Whitelist maps
See my presentation SQL Injection Myths and Fallacies where I give details on everything you need to know to defend against SQL injection.
I also cover SQL injection in my book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
There are certain instances when prepared statements cannot be used. For example, when you must dynamically generate the contents of an IN() clause, you cannot do WHERE col IN (?) if you have dynamically chosen the comma-separated values to go into the IN(). Also, if you need to dynamically generate the columns list in your SELECT clause, you must do it by building up the SQL string.
Bottom line is, both have their place. Prepared statements are excellent for predetermined queries, or queries that must be executed multiple times. Escaped dynamic SQL is excellent when 1) you must have maximum flexibility and 2) you don't forget to escape all your input.
I've been looking at how best to protect against sql injection in PHP/mysql beyond just using the mysqli/mysql real escape since reading this Is mysql_real_escape_string enough to Anti SQL Injection?
I have seen this very good thread How can I prevent SQL injection in PHP?
I use to do alot of ms sql server stuff on the desktop/internal tools, we always wrote stored procedures to protect against this so I read up on the equivalent in PHP/mysql using PDO http://php.net/manual/en/pdo.prepared-statements.php
In the above there is the line :
The parameters to prepared statements don't need to be quoted; the driver automatically handles this. If an application exclusively uses prepared statements, the developer can be sure that no SQL injection will occur (however, if other portions of the query are being built up with unescaped input, SQL injection is still possible).
I've been lead to believe that PDO do protect against sql injection attacks so can anyone provide a instance where PDO isnt sufficient from a security standpoint?
You can still get SQL injections from stored procedures which are internally using the PREPARE syntax (in MySQL) to create dynamic SQL statements.
These need to be done with extreme care, using QUOTE() as necessary.
Ideally, we should not need to use PREPARE in stored routines, but in certain cases it becomes very difficult to avoid:
Prior to MySQL 5.5, the LIMIT clause cannot use non-constant values.
Lists used in an IN() clause cannot be (sensibly) parameterised, so you need to use dynamic SQL if this pattern is used
It is sometimes desirable to use dynamically generated ORDER BY clauses.
etc
In the case where it is necessary to use PREPARE, then I would recommend, in order of preference:
If something is an INT type (etc) it is not susceptible to SQL injection, and you can place the value into the query without a problem (e.g. for LIMIT)
String values can be placed into an #variable before the EXECUTE, or passed in to the EXECUTE clause
List-values (for example for IN()) need to be checked for validity.
Finally, QUOTE() can be used to quote string values, which can be useful in some cases
It's not the structure you use (stored procedures, prepared statements etc.) that is decisive, but whether you are at any point concatenating SQL together using unchecked user input. For example, you can execute dynamic SQL from within a stored procedure, in which case the danger is still there.
The easiest way (from the injection-avoidance point of view) is to use SPs or PSs with bound-in variables: these do not need to be checked as they will be recognized as values to go within a predefined placeholder.