This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to prevent SQL injection in PHP?
Consider this snippet,
*mysqltest.php*
$user_id= $_GET['user_id'];
$user_id= mysql_real_escape_string($user_id);
$query = "SELECT * FROM people WHERE uid=".$user_id;
echo $query;
//then execution ..
Now, when we call this script as "mysqltest.php?user_id=56 or 1"
The query would be,
SELECT * FROM people WHERE uid=56 or 1 and result in successful injection.
So how can we protect ourselves at such situations where the column is of numeric types?
Is there any other work arounds at these situations without using pdo or prepared statements?
There are no "other issues" for the mysql_real_escape_string.
Most people confuse this function, thinking it's intended to protect them from injection by "cleaninig" somehow whatever "user input".
This is wrong for sure.
mysql_real_escape_string intended to format strings, making them good for the SQL query. That's all. As a side effect it makes injection impossible too.
So, every time one is going to add a string into query dynamically, they have to follow both string formatting rules, no matter of the data source:
enclose the data in quotes
escape special characters in it
as they are useless one without another.
Though it's all right to treat numbers as strings.
So, for the given example you cat treat your data as a string, as it was suggested in the deleted answer
$user_id= $_GET['user_id'];
$user_id= mysql_real_escape_string($user_id);
$query = "SELECT * FROM people WHERE uid='$user_id'";
the only exception is a LIMIT clause parameters that have to be integers only.
the only solution in this case would be to cast the data to desired format manually
$user_id= $_GET['user_id'];
$user_id= intval($user_id);
$query = "SELECT * FROM people WHERE uid=$user_id";
but remember, there are other cases, which require different treatment
One more thing to add.
It is boring to format your data manually.
Much better to let some code to do it for you, making code both short and safe
Related
I am trying to inject into a dummy website I have made, its a simple form which uses the text input to send data to my php file and then outputs the data gathered. The following is my code for the SQL.
$id = $_GET['id'];
$id = $_GET['id'];
$data = $conn->query('SELECT * FROM users WHERE username = ' . $conn->quote($id));
foreach($data as $row) {
echo $row['id'].' '.$row['username'];
}
When I try to use things such as unions I get no data back and if I put an apostrophe at the end of the URL I don't get a MySQL error. Could someone please explain why the site is secure from SQL injections?
As there are some confusions as to what I asked my final goal is to be able to get into the information schema so I have been trying to use statements like to get into the schema but without success:
' and 1=1 union select table_name,table_schema from information_schema.tables where table_schema='users' #
Other apparently may have missed what you were asking...
You are INTENTIONALLY trying to sql-inject your own site, such as for personal learning on how NOT to, but also see what impacts sql-injection CAN do. If so, take a look at your statement and see "what would I need to add to fake it out".
"SELECT * from users WHERE username = '$id'"
If the user puts a value such as "Bill" for the $id, it would become
"SELECT * from users WHERE username = 'Bill'"
and run no problem. Now, you want to inject and see ALL users, a common way is to close the quote and then add something else that will always return true... such as a user puts a value of
' OR 1=1 ;--
The above would result in
"SELECT * from users WHERE username = '' OR 1=1;-- '"
The semi-colon and dashes are important to "finish" the original query, and then indicate that anything after the dashes are comments so it won't try to execute anything AFTER the otherwise dangling close quote from your original query build construct.
Hopefully that helps answer why you may be failing while TRYING to inject into your own site.
COMMENT FEEDBACK
I don't know why my version would not work, I am not trying to union anything, just force an all records returned.
With respect to your UNION clause, that looks ok, but if your users table has 3 columns and your UNION is only 2 columns, that should fail as the union should be the same number of columns as in the original query. THAT would cause a failure on execution, but not enough specific information to confirm.
Most likely, you have magic quotes enabled, which is saving your otherwise-vulnerable code.
Don't rely on it.
As it can be clearly seen from either question and answers, most people don't understand what injection is. For some strange reason everyone takes injection consequences for injection itself. While injection is just a query creation. No more, no less.
So, the result of injection is not whatever data returned, but mere SQL query string. Thus, what the OP have to check is the resulting SQL query. It is extremely simple a task, as primitive as just echoing the query string out. This will reveal injection possibility immediately, without toilsome guesswork and sophisticated query building.
Simple output like this
SELECT * from users WHERE username = 'Bill\''
will tell you that magic quotes are on and whole question is a thousand-times-duplicate and not-a-real-one at once.
UPDATE
For some strange reason the code in the question mysteriously has been changed to invulnerable PDO based code. Which leads me to believe that whole performance were just a mere trolling.
Your injected SQL String should look like this
-1'/**/UNION/**/SELECT/**/1,##VERSION/**/FROM/**/users/**/WHERE/**/1='1
as you need to close the last ' in the final sql query
Update:
like Your Common Sense pointed out
For some strange reason the code in the question mysteriously has been changed to invulnerable PDO based code. Which leads me to believe that whole performance were just a mere trolling.
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.
This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
What is SQL injection?
XKCD SQL injection - please explain
I own a company and am wishing to know the runabouts of PHP while I am hiring for people to maintain and extend code for its websites, I was looing up on security on SQL injections and do not quite understand how these codes can affect the SQL database as they are in contained strings to the query.
How may one show the security flaw, so that I can see for myself what is happening?
Although much of this can be explained online, I have a feeling to explain this in a bit more detail.
If you can visualise what the string will become, you will understand the underlying risks of the script you are writing, in that it will become something else before it is actually ran.
A trivial malicious example:
$fromtheuser = "foo'; DROP TABLE affiliates; --";
$q = "SELECT id FROM affiliates WHERE website = '$fromtheuser'";
Can you explain what this will become? The developer couldn't.
"SELECT id FROM affiliates WHERE website = 'foo'; DROP TABLE affiliates; --'"
The key portions of this vector being '; to end the SQL string primitive, and --' to comment out the pushed quote.
What you would be looking for in the code written, is proper sanitization and filtering of the data before it is even placed in to the query. One important tool to help secure this front is some form of prepared query, in such you can bind the parameters to the query so that there is nothing to manipulate.
We can achieve this with prepared statements in either MySQLi (improved) or my personal favourite PDO. A quick rundown of what they look like.
mysql_real_escape_string (can handle encoding and quotes)
<?php
$clean = mysql_real_escape_string($q)
MySQLi:
<?php
$clean = $mysqli->prepare("SELECT id FROM affiliates WHERE website = ?");
$clean->bind_param('s', $website); //'s' for 'string'
PDO:
<?php
$clean = $pdo->prepare("SELECT id FROM affiliates WHERE website = ?");
$clean->bindParam(1, $website); //you can use :site rather than ? and 1
mysql_query("SELECT * FROM users WHERE username = '$username'");
Look what happens when we set the $username variable to something nasty:
$username = "' OR 1";
The query becomes:
SELECT * FROM users WHERE username = '' OR 1
An attacker can use improperly escaped variables to inject SQL into your query, effectively bypassing any (poor) security measure you have in place.
More info here
It's also worth noting that there are tools through which you can test whether your site is susceptible to injection attacks.
Take a look at
http://sqlmap.sourceforge.net/
The danger coming from the fact, that SQL query being a program.
So, malicious code can be injected in place of some operator.
string being delimited by quotes. if a delimiter inside of a string weren't escaped, it will actually close string statement and the rest going to be malicious code.
not only strings being dynamic. No delimiter will protect LIMIT $amount if $amount will contain malicious code, as well as ORDER BY $order.
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
Hey there, I'm doing some queries to a MySQL database that involves some user-input.. And I was wondering how to prevent injection attacks, like, for example, if someone were to type
"; a-MySQL query here;"
It would be executed, this would allow people access to compromise my system. I'm wondering how i could protect against things like this, possibly create a function that filters out the query looking for bad statements? or perhaps not allowing the ; character?
To sum it all up:
Whats the common practice now adays to prevent injection attacks?
How should I go about it?
How effective will this solution be against people who know what they are doing from changing my database.
The only way is to properly escape user-submitted data. Others have pointed out some ways of doing so.
There's another way: prepared statements and placeholders. Prepared statements are supported by every modern PHP database interface, including mysqli and PDO.
Let's use PDO as a demonstration. Let's say we wanted to update a bit of data in the table foo submitted by a user.
$sql = 'UPDATE foo SET bar = ? WHERE user_id = ?';
$sh = $db->prepare($sql);
$sh->execute(array( $_POST['bar'], $_SESSION['user_id'] ));
The variables in the array passed to execute replace the question mark placeholders in the query. When this happens, they are automatically escaped and quoted. You don't need to manually escape them to make them safe to put in the database!
On the other hand, you will still need to filter them for unexpected content, like HTML, Javascript, letters where you expect numbers, etc. Making data safe to insert into the database is only half of the battle.
An even better way than calling a mysql_escape_string variant is to use bound queries in PDO, so that it's impossible to forget or miss a case since PDO will do the escaping for you when circumstances require it.
See for more:
http://www.electrictoolbox.com/php-pdo-bound-placeholders/
They best solution is to run every piece of input from the wild (user) through this function PHP.net: MySQL Real Escape String This will clean up most - if not all - Injection issues seen today. You would simply need to do something like the following:
$var = mysql_real_escape_string($_GET['var']);
$query = "INSERT INTO foo (var) VALUES (\"$var\");
It's always good practice to force type casting on variables you know should be a type. For instance a numerical identifier:
$id = (INT)$_GET['id'];
or
$id = ( is_numeric($_GET['id']) ? (INT)$_GET['id'] : -1; // replace -1 with FALSE, or NULL, etc
Which will force that variable to be an Integer so you won't end up with $id being "foo" or some other non-numeric.