PHP: Understand SQL Injection - php

I'm working on old website and I found this error in log files:
Invalid SQL: SELECT COUNT(*) AS color_count FROM colors WHERE id IN (on,on) ;
mysql error: You have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near
'on,on) ' at line 1
The code php is like that :
$query = "SELECT COUNT(*) AS color_count FROM colors WHERE id IN ";
$ids = implode("','", $_GET['id_color']);
$query .= "('".$ids."') ";
I resolved this error by adding mysql_real_escape_string.
But I want to understand how an SQL injection can modify the query and remove the simple quotes ' from the query?

SQL injection can only add characters, it cannot remove characters from your SQL string. In other words, it's not "SQL suction". :-)
I can think of these possibilities:
The error in the log occurred on a date in the past, before your code did quoting. Perhaps it was originally designed to handle only integers, which aren't required to be quoted.
I recommend noting the date/time of the error in the log, then retrieve the version of code from your source control corresponding to that date.
The error was generated by a similar SQL query in another part of your code, where the code fails to quote the values.
I recommend searching all of your code for similar SQL queries.
Your code (or your framework) strips single-quotes out of the SQL string. I can't guess why it would do this, but in theory it's a possibility.

SQL injection is a danger (among other cases) anywhere you allow user input to be put directly into the statement. This is why bound statements are more secure and preferred.
The gist of it is, if I can tack on input to the end of your statement, there's nothing to stop me from adding a semicolon to end your current statement, and then a new statement in the same variable. So my string could be:
"11;Drop colors if exists cascade" which a naive execute would execute two statements, one of which completes as you expect, then the malicious one which deletes your table.
Now, a checkbox isn't likely to be a victim of injection, but it should always be a concern.
Do some more research on SQL injection, and really understand it. Then you can start building and modifying code to better combat it.
http://en.wikipedia.org/wiki/SQL_injection

It can be ' or '1'='1 if you want to break single quotes (http://en.wikipedia.org/wiki/SQL_injection).

Related

MySQL injection query

I'm familiar with prepared statements and I know that they are best practice when it comes to protecting against MySQL injection. But I'm wondering how this PHP/MySQL statement could be at risk of an injection attack:
$result = mysqli_query($db,"SELECT name FROM users WHERE id = '".$_POST['name']."';");
It seems to me like the input from the user would be contained inside the single quotes. Can you execute more than one query in one mysqli_query statement?
Also, is making the above safe just as easy as this...
$result = mysqli_query($db,"SELECT name FROM users WHERE id = '".mysqli_real_escape_string($_POST['name'])."';");
It seems to me like the input from the user would be contained inside the single quotes
It would unless you include single quotes in the posted name, which would allow you to break out of the quotes. Example, post the name as:
' or 1 or '
The WHERE clause becomes:
WHERE id = '' or 1 or '';
This would match and retrieve all rows in the table because of the or 1 part. As you can see, it breaks out of the quotes to inject some SQL, then it goes back into the quotes to make the query valid.
Can you execute more than one query in one mysqli_query statement?
No, but if it was executed with mysqli_multi_query then yes you could add multiple queries on to the end.
is making the above safe just as easy as mysqli_real_escape_string?
Generally yes but a Prepared Statement would be better. Using escaping, the WHERE clause would become (using my example above):
WHERE id = '\' or 1 or \'';
This is no longer vulnerable because the quotes can't be broken out of, and would only match rows if the name literally matches ' or 1 or ' which is obviously unlikely.
It seems to me like the input from the user would be contained inside the single quotes
All the attacker has to do is put a single quote inside the name POST data, and it won't be any more.
name=' OR 1=1
Also, is making the above safe just as easy as this
That looks OK … but it hurts my eyes. Use prepared statements. They are much easier to read then SQL built by concatenating strings together.
Basic explaination:
If you simply insert $_POST['name'] into the query as per your first example, the resulting SQL string will be invalid if the name variable contains a single quote character.
This will immediately annoy anyone named O'Brien, or similar.
But this can then be exploited by a hacker, who could modify his "name" to include valid SQL code after the single quote. This could be any valid SQL, allowing the hacker to do anything to your DB or query anything from it. Exactly what he can do would depend on other factors in your code, but suffice to say that even in the best case scenario, he could do some pretty devastating things.
To answer your second question: Yes. Escaping using mysqli_real_escape_string() will mitigate this problem.
However, to take things one step further, you might also want to investigate using Prepared Queries, which is a feature of the mysqli extension. This can make your code a lot neater as it avoids having to use that nasty long mysqli_real_escape_string() function name all over the place. It also has other benefits such as improved query caching.
Hope that helps answer the question.
What if I passed the following value for $_POST['name']?
'; DELETE FROM users WHERE name <> '
I would be closing the first single quote, then introducing the damaging query which just has a single open quote at the end, which would be closed by the single quote in your original query.
You second query is fine. Though you really ought to consider use of prepared statements (which are supported by mysqli)
If you're using mysqli you should always be using the SQL placeholder method for doing this. The escaping functions are the hard way.
$stmt = $db->prepare("SELECT name FROM users WHERE id = ?");
$stmt->bind_param('i', $_POST['name']);
$stmt->execute();
If you don't understand the risk here, you really need to read up on SQL injection attacks in general, and read what automated hacking tools can do to those that aren't cautious enough.

SQL injection on INSERT

The SQL Injection on INSERT as described here doesn't seem to work with MySQL.
SQL injection on INSERT
When I use this statement:
INSERT INTO COMMENTS VALUES('122','$_GET[value1]');
With this as the 'value1' variable value:
'); DELETE FROM users; --
This error gets returned:
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELETE FROM users; --')' at line 1
What's wrong???
PS: Someone suggested me to do an SQL injection with this as variable value:
',(SELECT group_concat(table_name) FROM information_schema.tables INTO OUTFILE '/var/www/tables.txt'))-- -
But it didn't work either, and returned a syntax error.
Your injection turns a single SQL statement (INSERT ...) into multiple SQL statements (INSERT ...; DELETE ...).
However, the PHP mysql API does not support multiple statements in a single query. (The underlying MySQL C API must be explicitly instructed to support this functionality, which your bindings do not do.)
As #pilcrow points out, mysql_query will only accept a single statement. Your example is actually two statements:
INSERT INTO COMMENTS VALUES('122','value');
and:
DELETE FROM users;
The PHP mysql API will reject this immediately, so you can't use that to test SQL injection.
Another problem with the statement is the use of comment characters. The MySQL Comment Syntax states that:
From a “-- ” sequence to the end of the line. In MySQL, the “-- ”
(double-dash) comment style requires the second dash to be followed by
at least one whitespace or control character (such as a space, tab,
newline, and so on). This syntax differs slightly from standard SQL
comment syntax, as discussed in Section 1.8.5.5, “'--' as the Start of
a Comment”.
So you have to have whitespace after the --. If you use # instead, no following whitespace is required.
A simpler and safer way to begin your SQL injection testing is to try a simple SELECT:
$value = "1' OR 1; -- ";
$sql = "SELECT * FROM mytable WHERE id = '$value'";
print "$sql\n";
// SELECT * FROM mytable WHERE id = '1' OR 1; #'
$result = mysql_query($sql,$con);
// Should return multiple rows (if your table has multiple rows):
while ($row = mysql_fetch_assoc($result)) {
print "{$row['id']}\n";
}
As the PHP mysql API rejects multiple statements, some of the more commonly used examples of SQL injection won't work, and that's a good thing. However, as you can see from the simple example above, it doesn't prevent other forms on SQL injection.
Think how a statement like this could be affected:
DELETE FROM mytable WHERE id = '1'
Also bear in mind that deleting data is probably not of much use to anyone other than a malicious hacker who just wants to cause disruption. Obtaining confidential data to which you are not supposed to have access, on the other hand, may be very useful.

php securing get against others

I have a php file which at the start, assigns some variables from what was sent using $_GET.
It then does some mysql queries, processes the output, then echos out some text and variables.
The only protection I have set in the code is mysql_real_escape_string on the GETs.
Is that enough to prevent attacks?
What else can be done?
Well, you take mysql_real_escape_string awfully wrong.
It's not your fault though - its one of wickedest delusions among PHP society. Even official man page put it all wrong.
This function has nothing to do with securing anything in general and GET variables in particular
This function is merely escaping string delimiters, to make string delimiters unable to break a string. Thus, 2 most important consequences:
not only GET variables but ALL variables being put into query in quotes should be processed with mysql_real_escape_string(), no matter of their source or origin or possible dangerousness
it will have effect on the quoted strings only. It's totally useless to use this function for any other part of query, LIMIT clause variables for example.
Thus, to secure your SQL query, you have to follow whole set of rules, not just deceiving "sanitize your data with mysql_real_escape_string".
You can learn how to protect your SQL from my earlier answer on the similar topic: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
update
a scenario to show why mysql_real_escape_string is not a silver bullet
being given with url
http://www.example.com/news.php?offset=99999+UNION+SELECT+password+FROM+users+--
a code
$offset = mysql_real_escape_string($_GET['offset']);
$sql = "SELECT title FROM news LIMIT $offset,20";
Will result if not in not so pompous as little bobby tables' one but in somewhat no less disastrous.
No, there are plenty of attacks that you might not have protection for. One example is CSRF. It's a big field, so I recommend reading up on this stuff on the Owasp site:
http://www.owasp.org/
Using this is definitely not sufficient. It is not even sufficient when you only consider sql injection. It is sufficient when you consider sql injection on strings only, but as soon as you have an integer (say an id) it goes wrong:
http://example.com/foo.php?id=10
Goes through:
$q = "SELECT * FROM foo where id = " + mysql_real_escape_string($_GET['id'])
Which results in de SQL query:
SELECT * FROM foo where id = 10
This is easily exploitable, for instance:
http://example.com/foo.php?id=10%3B%20DROP%20TABLE%20foo
Goes through:
$q = "SELECT * FROM foo where id = " + mysql_real_escape_string($_GET['id'])
Which results in de SQL query:
SELECT * FROM foo where id = 10;DROP TABLE foo
I hope this clarifies why it isn't enough.
How you should solve this? Define what input is allowed, and check that the input is indeed of that form, for instance:
if(preg.match("^[0-9]+$",$_GET['id']){
// your code here
}else{
// invalid id, throw error
}
But the best way to be on the safe side (regarding SQL Injection) is using prepared statements:
http://php.net/manual/en/pdo.prepared-statements.php
mysql_real_escape_string will only protect you agains SQL Injections when you use the return value in a MySQL string declaration like:
'SELECT foo FROM bar WHERE quux="'.mysql_real_escape_string($val).'"'
It won’t protect you if you use it in any other context (specifying the sorting order with ASC/DESC, row limits, table/row names, etc.). In that case you’ll need to validate/filter/sanitize the value separately.
And if the user data can also be part of the query result that you are about to output, use htmlspecialchars to replace any HTML special character.
you have if the get variables have values using the isset() and empty() functions

Select keyword cant be inserted in to mysql table

I am using mysql database for an application. I get some user details. Once user uses select keyword in his answer, the Insert query causes problems in mysql. I am using nearly 300 insert queries in my over all application. Select keyword makes problem.
How to solve it in easy way?
Thanks in advance
UPDATED:
$query = "INSERT INTO `feedback_entry_mailactivity_log` ( `subject`, `body_text`, `to_mail_id`, `from_mail_id`, `cc_mail_id`, `created_user_id`, `created_date_time`, `last_updated_user_id`, `last_updated_date_time`, `feedback_entry_id`, `feedback_id`, `account_id`, `section_id`)
VALUES ('".$subject."', '".$body_text."','".$to_mail_id."','".$from_mail_id."','".$cc_mail_id."','".$assign_to_userid."', NOW(),'".$assign_to_userid."', NOW(),'".$feedback_entry_id."','".$feedback_id."','".$this->account_id."','".$temp_sectionid."' );";
$this->db->execute($query);
In this case if $subject="select a tag";
Thus when I use keyword select insert query doesn't works
The problem is the use of string-generated SQL statement -- this can lead to incorrect escaping and injection attacks (or mis-behaviors) leading to errors like above. Imagine if one of the input variables -- the one with 'SELECT' in it -- contains the SQL string-escape character such as Wish this would' SELECT FAIL. (This might not be the exact problem in this case and the real problem could lay with some other layer trying to "protect" the use of the bad access method(s).)
To fix this problem correctly use PDO (or similar) and prepared-statements. (Jeremiah Willcock suggested mysqli_prepare).
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).
Note: Incorrect "solutions" include mysql_real_escape_string and similar. There are very few -- perhaps none for static DQL -- cases when "manual escaping with SQL string-building" approaches like this should be used.
Happy coding.

Are PHP MySQLi prepared queries with bound parameters secure?

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.

Categories