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.
Related
Suppose you have a query looking like this:
SELECT * FROM messages WHERE sender='clean_username'
where the clean_username is received over get/post and sanitized like this:
$clean_username = preg_replace( '/[^A-Za-z0-9_]+/m' , '', $dirty_username );
The above code removes any whitespace (among other things), which means that the valid_username parameter will always only be one word.
What is the simplest way this can be exploited with an injection?
I'm asking this question to better understand how SQL injection works. In my work I stick to the established good practices of using prepared statements and parameterized queries to prevent injections, but I think it's good for people to also have an understanding of how malicious code can be injected in a simple scenario like this.
You can still exploit this using hex coding: stripping spaces is not enough.
I guess this is a somewhat interesting place to start. But consider that preg_match()es are pretty bad for performance on high traffic sites.
Prepared statements and parameterized queries are always the best way to prevent SQL injections.
Example of GET injection using hex coding and no spaces
?id=(1)and(1)=(0)union(select(null),group_concat(column_name),(null)from(information_schema.columns)where(table_name)=(0x7573657273))#
I think you can see the problem above.
I think you already answered the question on your own.
The best way is a standard approach where you use parameterized queries to distinguish between user data and sql command.
In your particular case you assume that a sender username can only consist out of a limited set of ASCII characters. That might work for the moment, and as long as there is no string conversion before, no one can easily close the string apostrophes within the sql statement.
But always consider anticipation of changes. Somebody can rely on your given code in the nearby future and use or modify it and make new assumptions. Your test is actually weak and it can suddenly become dangerous when no one remembers and expects it.
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.
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 3 years ago.
I'm working on a site that has been hacked through SQL Injection (at first glance only db entries are corrupted with cross-site scripting) the potential vulnerability I found after looking at the code is that there's a lot of mysql_query call whose inputs are not escaped at all.
The good old :
$query = "SELECT * FROM mytable where name LIKE '%".$_GET['name']."%'"; /*HACK HERE*/
mysql_query($query, $connection);
Nevertheless I can't find how can we do something cool from that injection vulnerability (by cool I mean something like an INSERT or an UPDATE). I've tried to build a statement like this one :
SELECT * FROM mytable where name LIKE '%' AND WHERE id IN (INSERT INTO secondtable (id,description) VALUES (15, 'Fifteenth description');--%'
No success. I guess that the INSERT has nothing to do here.
I'm escaping all user's inputs in the code right now but I've not really get how hackers have penetrated this site, then I'm not 100% sure that my fix will do the job. Any brilliant suggestions ?
Thanks
Depending upon the version of mysql you are using, and the setup of the connection, mysql_query may allow more than one statement.
You should look at how the connection is being created, and for any usage of mysql_set_server_option.
Because mysql_query is not supporting multiple queries, So any injection that is doing like '; DROP TABLE mytable; -- won't be successful.
However, the attacker can combine with other select statement to select the other info like password info.
Interesting that your question hasn't received many (correct) answers yet!
As you discovered, usual PHP MySQL APIs like mysql_query, mysqli::query etc. only execute the first SQL statement in case one passes several of them (separated by semicolons), as would an attacker using the most common class of SQL injections.
Defender tip: banish mysqli::multi_query and friends from your code; the minute performance improvements are not worth the risk.
Does this clever move by PHP-folk completely close all attack channels on some code that goes like "SELECT yadda yadda" . $_GET["untrusted"]? Not quite. As knittl remarks, even a pure-DQL SELECT can be used to escalate one's privileges by UNION ALL SELECT'ing from any nearby interesting table, including but not limited to the passwords table. There are cheat sheets out there giving enough tips and tricks to basically extract the entire database this way.
Defender tip: apply defense-in-depth with known-good techniques:
input validation
whitelist indirection in an associative array
prepared statements (if you can make sure that they are effective and not just string escaping in disguise!)
an ORM
protective encoding (e.g. Base64) when the untrusted input cannot be satisfactorily sanitized (e.g. a blog post that may legitimately contain SQL-sensitive punctuation)
or as a last resort only, string escaping
Next, one may observe that not all DQL is side-effect free, in particular when it ends with INTO DUMPFILE somethingsomething.
Defender tip: always configure secure_file_priv in your MySQL / MariaDB server.
Last but not least, even an attacker who is in a position to inject arbitrary SQL is limited by the authority granted to the Web app as a whole.
Defender tip: secure your app by applying POLA.
Only GRANT the Web app's MySQL user as much authority as it needs. It is not a bad idea to design your app so that it requires no DDL at all. If you must provide a “back up / restore DB” feature or some such from the Web UI, use a separate MySQL user for that.
Automate backups, even though they are useless — restores are what matters.
Possible scenario 1
Weak passwords/hashing will let an attacker to select administrator's password.
It would be wise to change all administrators passwords.
Now, it's been a while since did any php, but in general most data access libs have some sort of parameterized sql to reduce the risk. A quick google came up with this for php:
http://php.net/manual/en/pdo.prepared-statements.php
The other poster have already described how to do a sql injection so I won't get into that.
I'm pretty sure that a hacker would be able to modify the query easily. Even if mysql_query() doesn't support multiple queries, there are ways around that. you could just use a mysql IF statement added on to the end, and of course that will execute a completely new query.
$query = "SELECT * FROM mytable where name LIKE '%".$_GET['name']."%'";
$_GET['name']="'; DROP TABLE mytable; -- ";
so
$query = "SELECT * FROM mytable where name LIKE '%'; DROP TABLE mytable; -- %'";
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 1 year ago.
I have a Single Page Application in which the browser does all the logic work. Except for initial loading, the server is pretty much a fancy interface to the database.
The browser sends data dictionary keys, column name / value pairs, and where clauses for SELECT, for example. The server assembles the parts into SQL, executes the queries, and replies. NEW: In a SELECT, for example, the table name and columns pulled are from the data dictionary - the browser supplies the data dictionary key and the SELECT where clause.
This very open environment is very susceptible to SQL Injection attacks. The goal is to prevent damage from said attacks.
Problems to be Overcome
First, as discussed, it is impossible to paramaterize a random SELECT where clause - SELECT cannot use a prepared statements.
Second, mysqli, the library for paramaterized statements with MySQL, does not support NULL nor MySQL functions, eg, CURRENT_DATE or NOW(), as discussed.
Proposed Solution
First, if SELECT cannot be paramterized, then execute SELECT by a user who has no DML or DDL rights. This will prevent SQL Injection Attacks from changing the database.
Second, write a wrapper function for mysqli that will allow NULL and MySQL functions to be passed as parameters. This will allow parameters to be easily used for all DML.
Third, shadow highly sensitive data where it cannot be seen or touched by normal queries or normal users. This will put sensitive data, such as passwords, out of the range of attacks.
Forth, write a wrapper order to enforce the user / query type relationship. This will ensure SELECT are executed by the select user, for example
The result of that effort is here. The question is, logically, will this approach successfully protect against SQL Injection Attacks?
Non-Answer Answers
I proposed this same question before. Due to my poor job of presentation, received answers and comments ended up focusing on spurious issues instead of addressing the (admittedly) difficult question - does this actually work?
As a reference, here are some of those comments and answers.
Use PDO's prepared statements
First, prepared statements cannot be used for all SELECTs - how does PDO help? Second, mysqli does not accept NULL or MySQL functions - how does PDO help?
Why re-invent the wheel
If you know of an approach that overcomes these problems, I would really really like to know - this is a difficult problem.
no mysql_real_escape_string() in sight
Values should be sanitized before being passed to the database query functions. mysql_real_escape_string() is one of a set of sanitizing functions available, for example, one would use a different sanitizer for dates.
too much work
Please share with me your know of any approach that overcomes these problems - I would really like better insight. That said, from my setting up the whole thing again, following my notes it took between 30 and 45 minutes. No time costs incurred thereafter.
I am happy with mysqli
How are you going to prevent SQL Injection Attacks when you cannot parameterize your SELECT? Do you expect to never use NULL when updating a column? Every man to his own poison, but these are issues I hope to solve - not live with.
#Konerack pointing out limited number of parameters
Right. Changed to code to use eval() (shudder) which solved the problem. Needs security review.
The Question Again
Will this approach protect against SQL Injection Attacks while overcoming the problems of no parameterized SELECT and mysqli's parameter limitations?
The answer is simple:
For parameters use PDO or mysql_real_escape_string().
For other dynamic SQL (table names, column names, syntax elements) use whitelisting.
See: How to prevent SQL injection with dynamic tablenames?
You cannot trust the browser to keep within the bounds you set in Javascript. This can be easily manipulated. So you must treat all data from that end as untrusted. Make sure you check all elements in the where clause against a list of allowed keywords.
For this purpose you'd use:
Check against whitelist for symbols: =, <>, >, LIKE, NULL, IS.
Check against a whitelist for boolean operators: AND, OR, XOR, NOT
All values must be properly enclosed in single ' quotes and you will need to feed these through mysql_real_escape_string() to make sure there are no shenanigans.
All values not in (1,2) and not enclosed in single quotes are column names, check them against a whitelist of allowed column names.
Reject all other input.
I am used to developing in PHP/MySQL and have no experience developing with SQL Server. I've skimmed over the PHP MSSQL documentation and it looks similar to MySQLi in some of the methods I read about.
For example, with MySQL I utilize the function mysql_real_excape_string(). Is there a similar function with PHP/SQL Server?
What steps do I need to take in order to protect against SQL injection with SQL Server?
What are the differences between SQL Server and MySQL pertaining to SQL injection prevention?
also - is this post accurate? is the escape string character for SQL Server a single quote?
Use PDO and parameterized queries and every database engine looks almost the same.
Use parametrized queries with ADODB or PDO. These libraries know the best escape function to use based on the database it is connected to. They allow you to switch between mysql and ms-sql without introducing vulnerabilities.
SQL Injection for MySQL and MS-SQL are radically different.
SQL Injection for MS-SQL is much more serious. For one you can stack queries:
select * from `table` where id='1' ; drop table `table`;-- '
Escaping is totally different, addslashses() does not stop sql injection under MS-SQL. It uses a double quote system so this is an escaped query:
select * from table where test='trying to inject '' didn''t work!'
A hacker can also access cmd.exe using xp_cmdshell from a sql query. Make sure this privilege has been removed!
Under MySQL you can't stack, so its common to use union select (only works when injection into a select, otherwise you can use a sub-select, but you can't stack a drop/update/delete/insert on top of a select):
select somthing from table where 1 union select password from mysql.user
Escaping is done with back slashes, addslashes() works most of the time, but mysql_real_escape_string() is a lot better.
select * from table where test='trying to inject \' didn\'t work!'
Also you want to disable file_priv otherwise a hacker might be able to drop a backdoor:
select test from table where name='jon' union select "<?php eval($_GET[e])?>" into outfile "/var/www/backdoor.php"-- '
it is not the tool that allows SQL injection attacks, it is the programmer and how they use it. both mysql and sql server allow you to get injected if you code incorrectly (blindly concatenate strings to create dynamic sql) and both provide parameter binding to avoid it.
No. There is nothing inherit in any database product to protect you against SQL injection because the problem is not rooted in the database. The problem is in the way outside applications formulate requests and send them to the database.
No, MSSQL provides no such function, and in Mysqli you shouldn't be using mysql_real_escape_string either. In both cases you should be using Prepared Statements or Stored Procedeures. I believe the PHP documentation provides ample explanation on how to use the MSSQL apis.
Parameterized queries are the way to go. The sqlsrv driver supports parameterized queries. Of course, this will only be useful to you if you are running PHP on Windows. On the chance that you are, there's more information (with examples) here: How and Why to Use Parameterized Queries.
With MSSQL you can utilize stored procedures to reduce risk of sql injection. The stored procedures would accept input in various types, so it would be hard to pass in string with sql commands.
also check out http://msdn.microsoft.com/en-us/library/ms161953.aspx
Any database can be subject to SQL injection attacks. One way to help avoid them is to use Stored Procedures. Both MSSQL & MYSQL support stored procedures.
Stored procedures. That's it. Deny access to any other operation other than executing stored procedures. This way you only allow a certain set of requests to be made to your database, thus eliminating any form of SQL Injection.
Both support Stored Procedures, BUT, in MSSQL they are easier to debug, because error messages are much clearer.