I've been reading about SQL injection attacks and how to avoid them, although I can never seem to make the "awful" examples given work, e.g. see this post.
I created a PHP file and a table in the database, had a value passed through $_GET and tried to delete the table by doing bob'); drop table students; -- and it didn't work. PHP automatically escapes the \' and the query has an error, no harm done. Same issue when trying to replicate login "attacks" like AND WHERE 1=1 etc.
example code:
<?php
$id = $_GET['id'];
$sql = "INSERT INTO Users (Username) VALUES ($id)";
echo $sql;
mysql_query($sql) or die(mysql_error());
And I'd pass sql.php?id=1); delete from Users; --
So is this some dated thing that used to apply in the days of PHP3 or something, and nowadays even novices are protected from things like magic quotes?
I'm using PHP5 on Ubuntu.
Quite the contrary. Magic quotes are deprecated in PHP5 and will be completely removed in PHP 5.4, as they brought more confusion to the programming world than they did good. Checking whether magic quotes are active, and escaping any SQL input scrupulously if necessary, is still very, very important... No reason to feel bad though, we've all been there, and my unknowing ass has been saved by magic quotes countless times :)
The PHP manual on magic quotes explains everything.
No this is still very relevant.
As are XSS and CSRF. Never underestimate the importance of proper input filtering.
Heh, you're saved in this case by having magic_quotes_gpc set to "on".
You'll be screwed soon.
The largest identity-theft in history was achieved in 2007 by exploiting an SQL injection vulnerability: see "SQL injection attacks led to Heartland, Hannaford breaches" (ComputerWorld, 8/18/2009).
OWASP reported in 2007 that injection attacks (of which SQL injection is one example) continue to be one of the most common software security problems.
You can also search for recent SQL injection News and find many cases reported every month.
However, the example in the XKCD cartoon isn't necessarily the most common type of exploit. Dropping a table by executing a second SQL statement in one request probably wouldn't gain the attacker much in the way of valuable data, it would just be vandalism.
Also, some query interfaces disallow multi-query by default anyway. That is, the database client API executes only a single statement given the SQL string, regardless of semicolons. This defeats the example shown in the cartoon.
note: PDO's query() method is known to support multi-query by default. So it is susceptible to the XKCD-style attack.
As other folks have pointed out, the more likely risk is that an SQL injection will alter the logic of SQL expressions, and apply your query to extra rows besides those you intended.
For example:
$sql = "UPDATE Users SET PASSWORD = MD5('" . $_POST["password"] . "'||salt) " .
"WHERE user_id = " . $_POST["userid"];
What happens when I send a request with parameter userid set to the string 123 OR userid=456? I would reset my own password (userid 123) as well as the password of userid 456. Even hashing the password with a per-user salt wouldn't protect against this. Now I can log into either account.
There are lots of ways SQL injection can be perpetrated.
Magic quotes don't take character encoding into account, and thus are vulnerable to attacks based on multi-byte characters.
As for it being a risk today, Google searches turn up countless vulnerable sites. An SQL Injection vulnerability was reported for Bugzilla around September 10. So, yes, sites are still at risk. Should they be? The tools are there to prevent injection, so no.
That particular attack doesn't work, as mysql_query will only execute a single statement.
I can still abuse your code though, e.g. if I arranged for id to be SELECT password FROM Users WHERE Username='admin' I might have a fighting chance of being able to get your system to expose some internal information.
Basically, if you allow unfiltered input into your SQL, there will be some very creative ways of both creating data you didn't expect, and exposing data you didn't intend!
Oh my.. SQL Injection is not a risk, it is a gaping security hole. It mainly exists in php because the API makes you want to interpolate any old data into your SQL queries.
When I see a site written in PHP or ASP, I can just smell the SQL injection vectors that they reek of. People try to secure their PHP apps with mysql_real_escape_string() and intval() and do similarly in other languages. This is a mistake. It's like coding in C instead of Java or Python, where in the former, you make one mistake and you're dead, but in the latter, only semantic flaws can exist.
I strongly urge people to use either mysqli with prepared statements, or anything else that is parameterized, substituting text into code and then interpreting it is just bad practice in the first place IMHO.
On another note, PHP's magic quotes is just silly, and thankfully, deprecated. It can only cause more harm than good. If you rely on magic quotes, it means your app will be owned when magic quotes is disabled. Similarly, it may break other apps that don't expect escaped strings in inputs.
This is very much an active risk, magic quotes tries to give you a solution but I prefer to always develop with magic quotes off. This way I have to make sure I actually escape the inputs myself. Who knows if magic quotes will be on or off on the server where the script is actually deployed.
This is still a big problem. You can't assume that magic_quotes is turned on in every PHP installation you might use.
To see if magic qotes is turned on and clear out the mess from magic quotes:
if ( get_magic_quotes_gpc() !== 0 ) { $foo = stripslashes( $foo ); }
Then cleaning your statements a little:
$foo = mysql_real_escape_string( $foo );
$sql = "select * from foo where bar='{$foo}'";
etc.
In fact, you're better off just strictly turning of magic_quotes if you have the ability to do so.
I hope that helps you.
The bobby tables example will not work with the mysql interface because it doesn't do multiple queries in one call. The mysqli interface is vulnerable to the multiple query attack. The mysql interface is more vulnerable to the privilege boost attack:
In your form I type account: admin password: ' or 1=1 -- so that your typical login sql: select * from users where user_name = '$admin' and password = '$password'. The or causes this to be true and let's you log in.
Can't PHP do query parameters? If it can (as I'd be surprised if it didn't), that is the one solution which mitigates ALL SQL injection attacks.
As I've mentioned several times on stackoverflow before, I am a strong supporter of PDO, just stop using the old fashioned mysql, do yourself and your clients a big favor and learn PDO (it's really easy) and take advantage of prepared statements and bound parameters. Even if you do not need prepared statements performance wise, you still get the security benefits.
Also, I will recommend crashing your entire app in the clients face if magic quotes is set to on. It's just a drain on resources designed to protect the dumb and annoy the smart. (it uses more cpu than escaping manually, because it encodes everything, even when you don't need it)
There are a lot of different ways to perform a SQL Injection and quite a lot of ways to bypass basic safety precautions.
Those attacks have been within the top 10 web application vulnerabilities (rank #2) according to OWASP.
For more information, please refer to Top 10 2007-Injection Flaws.
No, and the less you worry about SQL Injection, the more likely you are to get hit by it.
Parameters passed to sql queries from the web pages ofen tend to be numeric IDs. For example let's assume you have an url http://foo.com/page.php?section=34 from which the section ID is used in a query like this:
SELECT content FROM sections WHERE section_id=$section;
No quotes to escape like in your example and whatever you'll put after the number in the URL will be passed to the query... So thew risk is real.
The simplest rule of thumb is to assume that all user input can be tainted. Check that data types are what you expect, variables are in the length/size ranges you were expecting, files are of the size and types you allow, etc. Other checks on non-external data can be warranted - before you call some important admin-level function, do a check - ($userlevel != ADMIN)?die():important_function();
There's always a bigger fish, or somebody who's a bigger jerk than you. Avoid assumptions about data and you've got a head start.
Not today yet, but it's only 20:34 UTC
Guardian jobs database attack demonstrates difficulties of database security, 06 Nov 2009
Guardian Jobs website hack may have been an SQL injection and not a 'sophisticated' attack, 27 Oct 2009
Whenever building up SQL from strings, SQL injection is a real danger.
I have also discovered that trying to avoid building up SQL from strings is a pointless endeavor. Sooner or later the full form of your SQL (not just things that could be parameters) must be generated at runtime.
I've have to develop for a server which has no way for me to disable magic_quotes! I include this on every page to undo the effects of magic quotes, so I can do proper escaping myself without \'double escaping\'. Even though I can taste vomit just from reading this, I haven't found a better solution.
if (get_magic_quotes_gpc()) {
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
As per OWASP 2017 Top 10, still Injection is the most happened and dangerous attack.
"SQL injection is always the number one risk. That is a reflection of just how many incidents are out there, as well as other factors that keep it very high up there" Troy Hunt - founder of breach site haveibeenpwned.com
Just to remember, using SQL injection we can dump entire database, controlling web server by uploading web shell, etc.
Related
I know this topic has been covered to death but I would like some feedback from the community regarding security within our web application.
We have standard LAMP stack web app which contains a large number of database queries which are executed using mysqli_query. These queries are not parameterized and at the moment but there is some naive escaping of the inputs using addslashes.
I have been tasked with making this system safer as we will be penetration tested very shortly. The powers above know that parameterized queries are the way to go to make the system safer however they don't want to invest the time and effort into re-writing all the queries in the application and also changing the framework we have to make them all work correctly.
So basically I'm asking what my options are here?
I've run mysqli_real_escape_string over the inputs. I've setup a filter which doesn't allow words like SELECT, WHERE, UNION to be passed in which I guess makes it safer. I know mysqli_query only allows one query to be run at once so there's some security there (from concatenating updates onto the end of of selects).
Do I have any other options here?
Edit: I should probably add that if anyone is able to provide an example of an attack which is completely unavoidable without parameterized queries that would also be helpful. We have a query which looks like this:
SELECT
pl.created
p.LoginName,
pl.username_entered,
pl.ip_address
FROM loginattempts pl
LEFT JOIN people p ON p.PersonnelId = pl.personnel_id
WHERE p.personnelid = $id
AND pl.created > $date1
AND pl.created < $date2
I've substituted a UNION query into the $id UNION SELECT * FROM p WHERE 1 = 1 sort of thing and I can prevent that by not allowing SELECT/UNION but then I'm sure there are countless other types of attack which I can't think of. Can anyone suggest a few more?
Update
I've convinced the powers that be above me that we need to rewrite the queries to parameterized statements. They estimate it will take a few months maybe but it has to be done. Win. I think?
Update2
Unfortunately I've not been able to convince the powers that be that we need to re-write all of our queries to parameterized ones.
The strategy we have come up with is to test every input as follows:
If the user supplied input is_int that cast it as so.
Same for real numbers.
Run mysqli_real_escape_string over the character data.
Change all the parameters in the queries to quoted strings i.e.
WHERE staffName = ' . $blah . '
In accordance with this answer we are 100% safe as we are not changing the character set at any time and we are using PHP5.5 with latin1 character set at all times.
Update 3
This question has been marked as a duplicate however in my mind the question is still not followed answered. As per update no.2 we have found some strong opinion that the mysqli_real_escape string function can prevent attacks and is apparently "100% safe". No good counter argument has since been provided (i.e. a demonstration of an attack which can defeat it when used correctly).
check every single user input for datatype and where applicabile with regular expressions (golden rule is: never EVER trust user input)
use prepared statements
seriously: prepared statements :)
it's a lot of work especially if your application is in bad shape (like it seems to be in your case) but it's the best way to have a decent security level
the other way (which i'm advising against) could be virtual patching using mod_security or a WAF to filter out injection attempts but first and foremost: try to write robust applications
(virtual patching might seem to be a lazy way to fix things but takes actually a lot of work and testing too and should really only be used on top of an already strong application code)
Do I have any other options here?
No. No external measure, like ones you tried to implement, has been proven to be of any help. Your site is still vulnerable.
I've run mysqli_real_escape_string over the inputs
Congratulations, you just reinvented the notorious magic_quotes feature, that proven to be useless and now expelled from the language.
JFYI, mysqli_real_escape_string has nothing to do with SQL injections at all.
Also, combining it with existing addslashes() call, you are spoiling your data, by doubling number of slashes in it.
I've setup a filter which I guess makes it safer.
It is not. SQL injection is not about adding some words.
Also, this approach is called "Black-listing" it is proven to be essentially unreliable. A black list is essentially incomplete, no matter how many "suggestions" you can get.
I know mysqli_query only allows one query to be run at once so there's some security there
There is not. SQL injection is not about adding another query.
Why did I close this question as a duplicate for "How can I prevent SQL-injection in PHP?"?
Because these questions are mutually exclusive, and cannot coexist on the same site.
If we agree, that the only proper answer is using prepared statements, then a question asks "How can I protect using no prepared statements" makes very little sense.
At the same time, if the OP manages to force us to give the positive answer they desperately wants, it will make the other question obsoleted. Why use prepared statements if everything is all right without them?
Additionally, this particular question is too localized as well. It seeks not insight but excuse. An excuse for nobody but the OP personally only. An excuse that let them to use an approach that proven to be insecure. Although it's up to them, but this renders this question essentially useless for the community.
I have seen a few people on here state that concatenating queries using mysql_real_escape_string will not protect you (entirely) from SQL injection attacks.
However, I am yet to see an example of input that illustrates an attack that mysql_real_escape_string would not protect you from. The majority of examples forget that mysql_query is limited to one query and use mysql_real_escape_string incorrectly.
The only example I can think of is the following:
mysql_query('DELETE FROM users WHERE user_id = '.mysql_real_escape_string($input));
This would not protect you from the following input:
5 OR 1=1
I would see this as incorrect usage of mysql_real_escape_string rather than a shortcoming, it is designed for strings not numeric values. You should either cast to a numeric type or if you are going to treat the input as a string when sanitising you should do the same in your query and wrap quotation marks around it.
Can anyone provide an example of input that can get around mysql_real_escape_string that does not rely on incorrect handling of numeric values or forget that mysql_query can only execute one query?
Edit: I am interested in the limitations of mysql_real_escape_string and not comparing it to alternatives, I realise there are better options for new projects and am not disputing that.
The main shortcoming of mysql_real_escape_string, or of the mysql_ extension in general, is that it is harder to apply correctly than other, more modern APIs, especially prepared statements. mysql_real_escape_string is supposed to be used in exactly one case: escaping text content that is used as a value in an SQL statement between quotes. E.g.:
$value = mysql_real_escape_string($value, $link);
$sql = "... `foo` = '$value' ...";
^^^^^^
mysql_real_escape_string makes sure that the $value in the above context does not mess up the SQL syntax. It does not work as you may think here:
$sql = "... `foo` = $value ...";
or here:
$sql = "... `$value` ...";
or here:
$sql = mysql_real_escape_string("... `foo` = '$value' ...");
If applied to values which are used in any context other than a quoted string in an SQL statement, it is misapplied and may or may not mess up the resulting syntax and/or allow somebody to submit values which may enable SQL injection attacks. The use case of mysql_real_escape_string is very narrow, but is seldom correctly understood.
Another way to get yourself into hot water using mysql_real_escape_string is when you set the database connection encoding using the wrong method. You should do this:
mysql_set_charset('utf8', $link);
You can also do this though:
mysql_query("SET NAMES 'utf8'", $link);
The problem is that the latter bypasses the mysql_ API, which still thinks you're talking to the database using latin1 (or something else). When using mysql_real_escape_string now, it will assume the wrong character encoding and escape strings differently than the database will interpret them later. By running the SET NAMES query, you have created a rift between how the mysql_ client API is treating strings and how the database will interpret these strings. This can be used for injection attacks in certain multibyte string situations.
There are no fundamental injection vulnerabilities in mysql_real_escape_string that I am aware of if it is applied correctly. Again though, the main problem is that it is terrifyingly easy to apply it incorrectly, which opens up vulnerabilities.
Ok, so apart from mysql_* being deprecated, I understand your wanting to know about any possible workaround that might exist. perhaps this blog post and the slides might reveal some of them.But as this older question here shows, casting and quoting isn't full proof. There's just So many things that can wrong, and Murphy's law, twined with that ever valid mantra "Never trust the network", will go horribly wrong.
Perhaps this article, but most importantly, the follow-up to that article can reveal even more security issues. To be honest, I know mysql_real_escape_string isn't fullproof, even in combination with type casting and string formats:
printf('WHERE id = \'%d\'',(int)mysql_real_escape_string($_REQUEST['id']));
doesn't cover every possible attack. I'm no expert on this matter, but what I can tell you is sanitizing every input, will, if anything, give you a FALSE sense of security. Most of the time, you'll know (initially) what and why and how you protect against the attacks, but your colleagues might not. They might forget something, and your entire system is compromized.
In summary: Yes, you might be able to prevent any form of malicious input from getting to your DB, but every additional action it requires is an added risk. In that scenario, the greatest liability (as always) is the developer that hasn't had is fourth cup of coffee on a monday morning. No code, no matter how defensive and well thought out, can protect itself from the monster that is a tired developer with a bad temper, going cold turkey on caffeine and nicotine.
This is my first time creating a PHP form that will run a MySQL query using INSERT INTO to store data in a production DB. Will this pass for "secure" or is it over-kill?
$orderText = $mysqli->real_escape_string(stripslashes(htmlentities((!isset($_POST["order_text"])?"undefined":$_POST["order_text"]))));
$stmt = $mysqli->prepare("INSERT INTO testtable (order_text) VALUES (?)");
$stmt->bind_param('s',$orderText);
$stmt->execute();
I'm not sure how the lack of a SELECT * affects the amount of risk I'm opening myself up to, but it seems like a script that only uses INSERT is safer. True?
There is a great amount of false assumptions in your question.
It is certainly an overkill.
Let's examine your extremely-hard-to-read zillion-nested-operator statement:
storing word 'undefined' makes no sense. A database has a special mark for the undefined fields - a NULL value. Or simply an empty string would be enough.
unconditional stripslashes adds nothing to security but may spoil the data.
htmlentities has nothing to do with SQL security, may help with site security in other aspects and may spoil the data as well.
escaping adds nothing to security and will spoil the data.
You are taking the problem from the wrong end.
Your primary goal is to format your query properly. Not to defend from imaginary "attackers" but to privent malfunction with most honest data. While properly formatted query will be invulnerable to various attacks just as a side effect.
Say, real_escape_string has nothing to do with security. It is used merely to format strings. There are no strings (data enclosed in quotes) in your query - thus this function is utterly useless (and even harmful).
In fact, an injection via INSERT is no less disastrous than via SELECT.
Finally, the right code would be
$stmt = $mysqli->prepare("INSERT INTO testtable (order_text) VALUES (?)");
$stmt->bind_param('s',$_POST["order_text"]);
$stmt->execute();
and when printing the order text back to the site, use htmlspecialchars()
that's all.
Variable binding, which you do on line 3, is sufficient to prevent injection attacks in general. Binding is a good idea and, in my opinion, should always be done. It has not only security advantages, but can yield a performance boost as well.
I would argue that performing the extra parsing at line 1 is actually a disadvantage:
It increases complexity, and some attacks take advantage of known data transformations, though using binding mitigates those as well.
I will recommend to treat everything that comes from a client, your visitors, as threat. Don't relax and only focus on some sql queries. Practicing a good habit has no limitation.
I agree with Charles, by binding the param you are already properly escaping the variable removing the chance of a SQL injection attack, and the complexity of line 1 is overkill. This will be come evident when you make the jump to PDO, because there is no specific $dbo->escape() call, as the escaping work is already completed with the prepare() / bind() calls.
I have started in web development not long time ago. I know some stuff now, but I'm really concerned about security issues that may arise. I know simple security solutions like preg_replace , but I'm not confident with that.
So I would like to ask you for any sort of speaking "universal" security standards that can be applied in the following cases. As I mentioned, I'm not pro so it would be great if you can start with something simple, yet useful. If possible could you provide examples please?
I did have a look at php manual, although I would like to know additional info from person.
Here are some typical MySQL / PHP things I use in my projects. Could you suggest any improvements to make them more secure?
$sql = mysql_query("SELECT * FROM stories WHERE showing = 1 ORDER BY cr_date DESC LIMIT 5") or die (mysql_error("There was an error in connection"));
while($row = mysql_fetch_assoc($sql)){
$story_id = $row["id"];
// etc...
}
$username = $_POST['username'];
$sql = mysql_query("INSERT INTO myMembers (username, //etc... )
VALUES('$username' //etc.. ")or die (mysql_error());
$username = $_GET['username'];
//gets username from url like http://myweb.com/profile.php?username=blabla
First of all, thank you for caring about web security. Many PHP developers don't know anything about it, and don't care to learn. They are the ones who are exposing our passwords and bank accounts to hackers. Be part of the solution! :-)
1. Treat the mysql extension as if it is deprecated.
Use the PDO or mysqli extensions instead. The plain mysql extension does not support prepared statements, and some other features, such as transaction control. No one should be using mysql if they have PDO_mysql or mysqli available to them.
2. Do not interpolate external data into SQL.
Anytime you get a value from $_GET or $_POST, you should consider it to be unsafe to use in any SQL statement, or shell_exec(), or other instance where you execute the string as some kind of code.
3. Use prepared query parameters instead of interpolation.
It's really easy. In fact, it's easier to use query parameters than it is to interpolate variables into SQL strings. You don't need to worry about escaping, or long complex string-concatenation.
See example code here: http://us.php.net/manual/en/pdo.prepare.php
4. For corner cases, use careful filtering.
A query parameter takes the place for one literal value in an SQL expression. Not table names, not column names, not SQL keywords, not lists of values or full expressions. For those, you do need to use string interpolation, but see my presentation SQL Injection Myths and Fallacies for examples of how you can "whitelist" values to interpolate.
Also check out the PHP filter extension, which offers a flexible way of validating inputs or stripping off invalid characters to make sure only the valid part of the input is used.
Looking at your examples, the SELECT query has no dynamic values interpolated from external sources like $_GET. So that one is safe.
The INSERT query takes a value from the request, which could contain malicious content that changes the way your query runs. This one is a good candidate for using query parameters.
Also consider that SQL injection is one of the two most prevalent security issues with PHP. The other one is Cross-Site Scripting (XSS). This is not directly related to SQL, but you should learn about it too.
Here's a good resource for learning more about web programming security: OWASP.org cheat sheets.
Many frameworks have a good set of security measures already in place that will do a great deal in preventing things like SQL injections. Yii, CakePhP, CodeIgnitre all may be of some use.
Although it's almost impossible to beat Bill, I feel I must clarify answers stating that "you have to trust no user input".
In fact, quite contrary - SQL injection protection will do any good only if it would be totally ignorant of the data source. And treat ALL the data as potentially malicious. And process it accordingly.
Thus, to summarize all the advises:
Prepared statements is a good approach but not a complete one.
It has a brilliant idea of using some placeholder, a proxy to represent the actual value in the query. Thus this value can be properly sanitized.
But these placeholders, as Bill said, are limited to the strings and numbers only. So, it would be a good idea to add another placeholder of your own - for identifiers. But you still have to watch out SQL syntax keywords manually.
So, instead of "Do not interpolate external data into SQL." statement one have to use
"2. Do not interpolate values into query directly but only by some proxy, performing necessary precautions"
The most important thing to remember is never trust anything from an external source (eg user input, responses from other web services etc). Always sanitise all input and where possible use code from your database provider to do so.
In the case of MySQL parameterising all queries is essential, so use a prepared statement, eg
$statement = $db->prepare('SELECT * FROM stories WHERE title = :title');
$statement->execute(array(':title' => $title));
$rows = $statement->fetchAll();
Your current insert statement is vulnerable to an SQL injection attack, modify it to be closer to:
$username = $_POST['username'];
$statement = $db.prepare("INSERT INTO myMembers (username) VALUES(':username');
$statement->execute(array(':username' => $username));
You should also ensure that you never store any passwords in plain text, always store a hashed version of a password (along with a salt) and check that the hash matches rather than the actual string. This means that should your database become compromised, figuring out your user's credentials becomes a non-trivial task.
These are only a couple of ways of making your app more secure, I'd highly recommend reading OWASPs top 10 site vulnerabilities and researching these individually as each one in itself is quite a big topic!
I thought I would get your opinion on an option I thought that would essentially eliminate the possibility of SQL injection (I think).
Currently, I have my admin account which obviously gives me full command of the database (alter, drop, etc). I than have an account that PHP uses which only has access to SELECT, UPDATE, DELETE, INSERT. What about setting up a user for each of those actions, and then just referencing the connect ID in the mysql_query statement - now obviously this would put a lot more strain on the server, having to make 4 connections perpage essentially, but if it security is important, it seems to me that this would be a valid option, by limiting the commands to EXACT function that you want performed in that case. Any thoughts on the viability of this option?
UPDATE: As I failed to mention earlier, this would NOT be the sole barrier of preventing SQL injection, mysql_real_escape_string(), prepared statements, etc. But I was just thinking maybe if by some how, ALL of these fail, wouldn't this at least limit the damage they could do? (e.g. on a registration form, they would be unable to SELECT hashes, or DELETE entries).
This is not what SQL Injection is all about. Any time you use parameters that haven't been sanitized in your SQL query you leave your database open to SQL injection, which might not necessarily have the goal of destroying data. It could also be to steal data or gain unauthorized access.
Consider a very restricted account where all it could do is SELECT. You write a query for authentication:
$sql = "SELECT COUNT(*) AS count
FROM users
WHERE user_id='{$_POST['user']}' AND pass='{$_POST['password'}'";
// check if returns a count of 1, if yes, log in
With normal input, you expect the query to look like:
SELECT COUNT(*) AS count
FROM users
WHERE user_id = 'username' AND pass='********'
Which should return 1 as the count if both username and pass match. Now an attacker tries to log in as admin. Since you haven't sanitized your inputs, they send $_POST['user'] as: admin'; --. The whole query becomes:
SELECT COUNT(*) AS count
FROM users
WHERE user_id = 'admin'; -- AND pass='********'
Everything after -- is a comment, so this ignores the other condition and returns 1 regardless. There you go, you've just granted a malicious user admin access. That is how some real attacks are carried out. You start with a low privileged account and through holes in security you try to gain access to more privileges.
Long story short, having an application-wide account with restricted privileges (eg: no DROP, ALTER, etc) is good. Never give anyone or any application more privileges than they need. But to prevent SQL injection, use prepared statements.
Having separate user with limited privileges is a common practice. But even if you will have such a user, it won't protect you from SQL injection attacks.
Consider the following example:
mysql_query('DELETE FROM posts WHERE user_id = '.$user_id
.' AND post_id = '.$_GET['post_id']);
Malicious user can trash all your posts with ease by setting post_id to 1 OR 1=1:
http://www.example.com/delete_post.php?post_id=1+OR+1%3D1
To get a valid protection you should always escape strings to be used in a query:
$_GET['post_id'] = mysql_real_escape_string($_GET['post_id']);
Or better use prepared statements.
This has very little to do with SQL Injection. You are talking about user permissions. Setting user permissions would have zero effect on protecting you from SQL Injections if you don't sanitize your user input and don't use prepared statements with PDO.
Read more about SQL Injections: SQL Injections and PDO
SQL injection and escaping sound magical to many people, something like shield against some mysterious danger, but: don't be scared of it - it is nothing magical. It is just the way to enable special characters being processed by the query.
So, don't invent new magial shields and ways how to protect the magical injection danger! Instead, try to understand how escaping of the input works.
The best would be just to have a look what escaping really does. Say the input string is:
');DROP TABLE table;--
after escaping:
\');DROP TABLE table;--
in fact it escaped only the single slash. That's the only thing you need to assure - that when you insert the string in the query, the syntax will be OK!
insert into posts set title = '\');DROP TABLE table;--'
It's nothing magical like danger shield or something, it is just to ensure that the resultant query has the right syntax! (of course if it doesn't, it can be exploited)
The query parser then looks at the \' sequence and knows that it is still the variable, not ending of its value. It will remove the backslash and the following will be stored in the database:
');DROP TABLE table;--
which is exactly the same value as user entered. And which is exactly what you wanted to have in the database!!
So this means that the if you fetch that string from the database and want to use it in the query again, you need to escape it again to be sure that the resultant query has the right syntax.
But, in your example, very important thing to mention is the magic_quotes_gpc directive!
This feature escapes all the user input automatically (gpc - _GET, _POST and _COOKIE). This is an evil feature made for people not aware of sql injection. It is evil for two reasons. First reason is that then you have to distinguish the case of your first and second query - in the first you don't escape and in the second you do. What most people do is to either switch the "feature" off (I prefer this solution) or unescape the user input at first and then escape it again when needed. The unescape code could look like:
function stripslashes_deep($value)
{
return is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);
}
if (get_magic_quotes_gpc()) {
$_POST = stripslashes_deep($_POST);
$_GET = stripslashes_deep($_GET);
$_COOKIE = stripslashes_deep($_COOKIE);
}
The second reason why this is evil is because there is nothing like "universal quoting".
When quoting, you always quote text for some particular output, like:
string value for mysql query
like expression for mysql query
html code
json
mysql regular expression
php regular expression
For each case, you need different quoting, because each usage is present within different syntax context. This also implies that the quoting shouldn't be made at the input into PHP, but at the particular output! Which is the reason why features like magic_quotes_gpc are broken (never forget to handle it, or better, assure it is switched off!!!).
So, what methods would one use for quoting in these particular cases? (Feel free to correct me, there might be more modern methods, but these are working for me)
mysql_real_escape_string($str)
mysql_real_escape_string(addcslashes($str, "%_"))
htmlspecialchars($str)
json_encode() - only for utf8! I use my function for iso-8859-2
mysql_real_escape_string(addcslashes($str, '^.[]$()|*+?{}')) - you cannot use preg_quote in this case because backslash would be escaped two times!
preg_quote()