If I am running a query on a MySQL database using PHP as in the following:
$query="SELECT * FROM tablename";
What is the best way to secure this from things like SQL Injections? I've heard about some escape methods, but won't it leave slashes in the query?
The query you have shown in the question doesn't use user supplied values so there is no case of SQL Injection but in a general case:-
Firstly, you must validate all the user input(usernames,emails etc.) before using it in a query. For ex:- If you have allowed only alphanumeric characters in a username, then you must check whether the input is actually alphanumeric or not before proceeding to form a database query and you must also check the size of all the inputs.
After that, in my opinion Prepared Statements is the best choice for preventing SQL injection.
Problem with mysql_real_escape_string():-
As mysql_real_escape_string() escapes characters according to default charset, so it is better than addslashes() function and it properly sanitizes SQL injections arising out of abuse of multibyte character sets, but in another article here, a workaround-scenario is shown that explains that injection can still be done.
Solution:-
So the proper and better way of preventing SQL injection is to use prepared statements. It is a technique in which SQL statements are precompiled before the insertion of the user-input (parameters) and are treated as reusable SQL templates. So, it separates the user input from actual SQL-Code and the SQL-parser never parses the user input.
Apart from security, it also optimizes the SQL query for speed. It helps in cases where you need to run same query multiple times with different user inputs.
You can refer to PHP manual for implementation details.
You shouldn't be doing a select * and should only get the fields you need.
You need to escape text that can be inputted by the user really or using data that is derived from such.
You need to use the mysql_real_escape_string().
first advice, never select *, only select the fields that are necessary, and if all of them are necessary, select individually, so when the project is continued by other developers, they would know whats going on more quicker. secondly, to secure a query use mysql_real_escape_string(); function and if HTML is being passed use htmlentities(); function
SQL Injection can be done, when you make something like this
$query="SELECT * FROM tablename WHERE Name LIKE '" . $_GET["name"] . "'";
Attacker can simply put SQL Injection in get parameter name - eg something like "' OR 1 OR '' = '"
Make sure every get or post parameter is passed thru mysql_real_escape_string or at least addslashes + intval .
$query="SELECT * FROM tablename WHERE Name LIKE '" . mysql_real_escape_string( $_GET["name"] ) . "'";
from your query i see that there is not security issue.
but, lets say that you want to involve a GET parameter in your query.
the worng way
$query="SELECT * FROM tablename WHERE id = ".$_GET['id']
here, you have a chance that some one will change the query.
so what you can do is use mysql_real_escape_string
the right way
$query="SELECT * FROM tablename WHERE id = '".mysql_real_escape_string($_GET['id'])."'";
this way you are protecting the parameter that has being sent by the user.
BUT
you should always verify each parameter coming from the user, and on top of that you secure it by the common way as shown above
I have used this code kindly see is it the right code and not injection able now
As far as I came to know is : injection code can be injected if we run the insert query?
kindly correct me i am not much educated programmer
$rs=mysql_query("Select * from subcat where CATID='".mysql_real_escape_string($_GET['cat'])."' order by ID ASC");
while($row=mysql_fetch_array($rs))
{
echo '<td align="left" style="text-decoration:none;padding-left:1px; ">'.$row['HEADING'].'';
echo '<td align="CENTER" style="text-decoration:none;padding-left:1px">BUY NOW';
echo '<td align="CENTER" style="text-decoration:none;padding-left:50px">Rs.'.$row['PRICE'].'';
echo '<tr><td colspan=5 style="border-bottom:1px #232323 solid;">';
}
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.
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.
I am getting this 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 ' address='xxxxx', city='sssssssss', pincode='333333333', state='Assam', count' at line 1
Thanks in advance.
http://dpaste.com/hold/181959/
your WHERE clause is wrong, you don't write WHERE a=1, b=2, c=3 you want WHERE a=1 AND b=2 AND c=3
additionally your logic is flawed, because your WHERE clause would usually be something more like WHERE id = x (at the moment you're updating a row in a table, where the row data is already the same as that which you're updating it to - if that makes any sense? :) )
furthermore, learn to escape your sql strings properly or you leave yourself vulnerable to sql injection
As well as the problem explained by oedo, you've also got severe SQL injection problems. You need to use mysql_real_escape_string to encode strings for insertion into an SQL statement, not htmlspecialchars. Or use parameterised queries.
htmlspecialchars() is for HTML-encoding text just before you output it into an HTML page. You should not HTML-encode strings for storage in the database.
Firstly, don't you have some kind of unique identifier for your users? Maybe a customer-id of some kind? You could use that to identify the customer in the WHERE clause to make your SQL more clear.
Secondly, do you expect that your user to write all the company EXACTLY like it is in the database? Because that is what you expect from them with your current design.
You need to identify the record by using an ID, not the field values. If you look to a lot of websites, usually they send the ID to identify a record. Like edit.php?id=1284, or view.php?id=1284, etc.
In short you will have a form that you fill up with the values that are in the database for that record ID. If you edit it, you write a edit query like:
$UpdateQuery = "UPDATE customer SET name = '" . $name . "', address = '" . $address . "' ....... WHERE id = " . intval($_GET['id']);
The reason I add intval is because that will only allow numeric values to pass through. As mentoined by bobince, watch out for SQL injections and let mysql_real_escape_string pass through all of your string values you enter in the query too.