There is a (ugly)project I was asked to help with by doing a specific task.
The thing is, I don't have any control over the other files in the site much less over the server configuration. Some data I'm going to use comes from a query like this:
'SELECT * FROM table where value like "'.$unsafe.'"';
$unsafe is an unescaped value coming from $_POST or $_GET.
I checked the server, is PHP5.1.6 and has magic_quotes_gpc On so the data is being auto escaped.
Is this query breakable? Being $unsafe between colons gives me the impression It cant be broken but maybe I'm missing something.
I know magic_quotes_gpc is deprecated because of its insecurity so I'm concerned about it, not because of the application security which fails every where but for my own knowledge.
EDIT: I'm aware of the security implications of *magic_quotes_gpc* and I never use it in my own projects. I always use parameterized queries to avoid injection but this time I was asked to add a very specific pice of code in a friend/client project, so I cant change what is already done. I'd like to know if there is a specific value I can use to create an injection so I can illustrate my friend why he should change it.
if the DB is mysql use mysqli_real_escape_string() instead, if the PHP version is very old you can use the mysql_real_escape_string (not recommended at the moment).
even if the variable is between colons it can be injected, you just need to close the colons inside the value of the variable and then inject whatever you want afterwards.
With regard to your edit: You asked "I'd like to know if there is a specific value I can use to create an injection so I can illustrate my friend why he should change it."
According to the manual page for mysqli_real_escape_string(), the characters it escapes are as follows:
NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.
The old mysql_real_escape_string() function also escapes the same characters.
This gives you a starting point as to which characters can be used to do injection attacks in MySQL. Magic quotes only escapes the quote characters and the slash character, which clearly leaves several gaping holes that can be exploited.
In an easy world, the above information would be enough for us to fix the escaping by doing a string replace on the remaining unescaped characters.
However, both the real_escape functions also require an active database connection for them to work, and this leads us to a further complication: character sets.
Further attacks are possible if the database has a different character set to PHP, particularly with variable-length character sets such as UTF-8 or UTF-16.
An attacker who knows (or can guess) the character set that PHP and the DB are using can send a crafted injection attack string that contains characters that PHP would not see as needing escaping, but which would still cause succeed in hacking MySQL. This is why the real_escape functions need to access the DB in order to know how to do the escaping.
Further resources:
Php & Sql Injection - UTF8 POC
SQL injection that gets around mysql_real_escape_string()
I hope that gives you a few pointers.
Related
Edit if you plan on answering this question please at least read it. Don't simply read the title, then Google 'sql injection php', and paste the results as an answer
First, I'm well aware that there are lots of resources available on how best to prevent SQL injection, but my question is specifically about if very little effort can be enough.
An organization I contract for was recently told that partners' (PHP) websites developed by their previous contractor have been found to have major security issues (my personal favourite is by using the string 'Emergency' in a URL you can gain unauthenticated access to any page in the site...)
I have been asked to review the security of a PHP site and highlight any major issues. I know from previous experience with this site that the standard of coding is truly awful (e.g. huge swathes of code duplicated across pages with around 5% variation, hundreds of unused variables, $var = "yes" in place of booleans, SQL statements written into every single script, unsecure by default (some pages forget to authenticate users), etc). Reviewing the site is a painful reminder that there are some real morons in the world that call themselves developers.
Because of these code quality issues I want to only highlight the serious and semi-serious security problems. If I note every problem with the site my review will take weeks.
I am not an expert on SQL injection, but it's my understanding that you must be able to close an existing query's quoted string before injecting any other kind of statement. So is the following line of code sufficient?
"'".str_replace("'","''",$_POST['var_name'])."'"
I'm not interested in suggestions about improving or changing the code, just whether it's possible in this scenario for SQL injection via $_POST['var_name']. If you're not familiar with PHP str_replace does replace all instances of the first argument with the second, not just the first instance.
Any input much appreciated.
No. In all honesty, if you are not preparing your statements, you are ASKING for a world of hurt.
Just because you escape your quotes with quotes, you are not protecting yourself. Think about this:
A user send you: username'; drop database foo;
You will escape it as username''; drop database foo;
But! if the user does: username\'; drop database foo;
You will be in trouble. You will resolve this for username\''; drop database foo;
Meaning the quote the user placed is escaped, and your quote ended the field username. The drop will then be execute. This is very unsecure.
You need to make sure you Prepare your statements or apply a quote command such as PDO::quote or mysqli_real_escape_string as these escape special characters.
You've got two options - Escaping the special characters in your unsafe_variable, or using a parameterized query.
$safe_variable = mysql_real_escape_string($_POST["user-input"]);
$mysqli = new mysqli("server", "username", "password", "database_name");
$unsafe_variable = $_POST["user-input"];
$stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");
Both would protect you from SQL Injection. The parameterized query is considered better practice, but escaping characters in your variable will require fewer changes.
To answer your question: no, simply removing quotes is not enough to prevent SQL injection.
For example,
"SELECT * FROM user_table WHERE userid=$userid AND password=$password"
Could go badly wrong if $password contained "garbage OR 1", or something like that. So if they've got badly formatted queries, removing quotes won't help.
It depends on your database server, and its configuration.
Doubling ' to '' is the ANSI standard way of escaping string literal content, and should be enough.
But, if you're using MySQL or versions of PostgreSQL before 9.1, string literal syntax is not ANSI-conformant by default (it can be, if reconfigured), and the backslash character has a special meaning. This permits attacks like the \' in 680's answer.
Additionally, if you're passing byte strings to your data access function, and your database happens to be using an East Asian character encoding, you'll have the problem that str_replace does a byte replace on a ' that may be part of a multi-byte sequence, ending up with a high byte followed by '': the first quote gets eaten as part of the multi-byte sequence, then the second closes the string. (This doesn't happen in UTF-8 which is designed to avoid trailing low bytes.)
Using the appropriate escaping call for the DB you are using (eg mysql_real_escape_string), or, better, using parameterised queries, means you don't have to know about these obscure and annoying edge cases.
I am working on a portal and I have these few questions regarding saving data in MySQL tables :
Should I save varchar field escaped ?
i'm using now mysql_real_escape_string() for avoiding string-injection.
Why should I save them unescaped (this was proposed by a guy on this website) and how would that work for characters like single and double-quotes. Doesn't it wreck the SQL command ?
easy talking around this topic.
And one last thing....I was using addslashes and stripslashes before using mysql_real_escape_string and it worked for me (of course, with mysql-injection of malicious code chance, which I recently discovered and documented myself on it)...
thanks
The very basic thing any programmer must learn is the meaning of context.
What am I going about here? If you knew the meaning of context, you wouldn't have asked this question. Now that (I hope) you know, you won't ask how to show <test> as HTML, or how to pass a variable to javascript.
So what's it all about? It's really easy. Context is the simple fact that something in a system may mean something entirely different somewhere else.
For example, in your case, a PHP string may mean something entirely different to MySQL. You can't just pass the string and expect everything to run smoothly - it won't.
So, now that you know what context means, you need to know something else that is important. You always need to convert a value from the older context to the newer one. Always.
Again, in your case, it's mysql_real_escape_string(), but a word of warning; conversion functions are context specific, so, for example, you can't use mysql_real_escape_string() to pass a string from PHP to Javascript. Similarly, you can't just use addslashes() and expect it to work. In fact, I'd argue that addslashes() is a completely useless and misleading function. Do NOT use it unless you are very sure of what you are doing.
Should I save varchar field escaped ?
No. You should escape data so that characters (in the data) with special meaning in SQL won't cause you problems.
Once it passes through SQL and gets stored in the database, it won't be escaped any longer.
i'm using now mysql_real_escape_string() for avoiding string-injection.
Don't do that, instead use prepared statements and parameterized queries
I was using addslashes and stripslashes
addslashes is a basic form of escaping. It is pointless unless you know exactly what the target of the data is. You should use something more specific where such a thing exists (and you are – mysql_real_escape_string)
stripslashes does the opposite of addslashes. Using them together is utterly pointless.
I'm working on a PHP comment system and came across the problem that the commentator's quotation marks are delimited when written to the comment file, so the output would end up like this for example:
"That is your father\'s! It\'s special to him!" (random sentence). How do I disable this?
The backslashes are added to the database query to prevent SQL injection. You can use the stripslashes() function to remove them when you retrieve the comments from the database.
You should also take a look at magic quotes.
It depends on version of PHP and it's configuration. Older versions (older than 5.3) had this enabled by default. It adds the quotes when you post your comment (so it will be stored in the database with the quotes). You can disable this behavior:
http://cz.php.net/manual/en/function.get-magic-quotes-gpc.php
http://cz.php.net/manual/en/function.set-magic-quotes-runtime.php
http://cz.php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc
For existing comments, you'll have to run some cleanup script that will fetch all rows, performs stripslashes() on it and save it back.
Escaping your queries should be done by mysql_real_escape() anyway, relying on magic quotes is suicide, so if you think about it, it's safer to turn them off completely and escape the queries manually.
Turn gpc_magic_quote 's off in your php.ini .
Those backslashes are there to escape the quotation marks from the SQL engine.
That style of programming is quite common with the mysql_* series of functions which take a string directly from the program and execute it directly in the database engine. This is notoriously prone to SQL injection attacks and, as you've discovered, corrupting your data. (When applied consistently, you can always de-corrupt the data on the way back to the user, with the stripslashes() function, but that also must be done consistently.)
The far better approach in my humble opinion is to use prepared statements and let the database libraries insert data directly into the database without any escaping or un-escaping involved. This also completely removes the risk of SQL injection attacks. (Though you're still free to write insecure code.)
There is a lot of talk about how addslashes and mysql_real_escape function are not safe to prevent injections. The truth is even the big frameworks or CMSs like Wordpress are using this functions and they do a god job so far.
I know there are some particular scenarios when using GBK charset, or utf8_decode can be used to inject some sql code, or some simple examples like 1' OR 1 -- that can be used when there is a simple where involved.
However, after a bit of research it seems very hard to inject something into a simple query with addslashes or mysql_real_escape used if the charset is UTF-8 and let's admit it, this is the most common scenario.
So, given this newbie script, pls provide a sql injection POC ( remember UTF-8 charset )
$mysql['username'] = addslashes($_POST['username']);
$mysql['password'] = addslashes($_POST['password']);
$sql = "SELECT *
FROM users
WHERE username = '{$mysql['username']}'
AND password = '{$mysql['password']}'";
Update - I just need a simple example not a full disclosure of the process. Even a link from google might work.
Update 2:
After further research, MySQL versions prior to 5.0.77 may be vulnerable to the GBK issue when combined with SET NAMES alone. It was earlier believed that only 5.0.22 and earlier were vulnerable.
This means that if you are using PHP versions prior to 5.2, in which mysql_set_charset / mysqli_set_charset were introduced, your code may be vulnerable under specific, well-crafted conditions.
If you're stuck on PHP 5.1, please ensure that you are using MySQL 5.0.77 or later. 5.0.77 is "only" two years old, but has been pushed into the repositories for RHEL/CentOS 5.x, the more popular distribution stuck with the 5.0.x series of MySQL and 5.1.x series of PHP.
Get upgrading, people!
Update 1: Another recent question has uncovered the source of the GBK thing: A bugfix in MySQL 5.0.22. Versions earlier than this are severely vulnerable when using anything other than mysql_real_escape_string combined with mysql_set_charset instead of just SET NAMES. The mysqli equivilent is named mysqli_set_charset.
There does not appear to be an equivilent of mysql_set_charset in PDO. This may be either because it can use MySQL native prepared statements, which may be immune from the problem, or whether SET NAMES is enough for their underlying escaping mechanism to work as expected.
Regardless, if you're using any MySQL version prior to 5.0.22 5.0.77 and are not taking extreme care to ensure that you're only passing in strings in a known character set, you may find yourself open to attack.
I'm leaving the rest of my original post unmodified, but I have updated the tldr.
There is a lot of talk about how addslashes and mysql_real_escape function are not safe to prevent injections
This is half correct. addslashes is entirely the wrong thing to use to protect against SQL injection because it is not guaranteed to provide the right escaping method for all databases, mainly because it adds backslashes and sometimes the escaping mechanism is entirely different.
If you're stuck in the ghetto of the prehistoric lump of crap known as the "mysql" extension (instead of using PDO or mysqli), mysql_real_escape_string is some of the best protection you've got when you need to concatenate together some SQL.
I know there are some particular scenarios when using GBK charset, or utf8_decode can be used to inject some sql code
You're probably thinking of creating malformed UTF-8 sequences, however I've only ever seen this as an XSS mechanism, never an SQL injection mechanism. Running strings through iconv with //IGNORE//TRANSLIT should be good enough protection (usually by truncating the string at the point of the bad sequence, which is an acceptable failure mode when you're being attacked -- malformed sequences should never happen in legitimate requests).
Further, while there are plenty of "quote" characters in non-Latin languages, MySQL is pretty decent at only actually obeying the backtick and double quote for identifiers and the single quote for string values.
Thinking about it more, perhaps there's some sequence of characters in another character set that might include a single quote in the middle, if taken as a different character set. However, it's very, very likely that addslashes is entirely ignorant of character set, and just works on the raw bytes. It'd stick a backslash in the middle of a sequence, and blow it up. However, that should just result in a whine somewhere along the lines about bad character set information.
mysql_real_escape_string, on the other hand, is designed with knowledge of the connection's character set built in, so it wouldn't escape the sequence if it sees the sequence instead of a quote. However, because it would recognize it as a sequence instead of as a quote, there's no danger at all.
Ultimately if you think this is a problem, it's your responsibility to ensure that you accept input in only the expected character sets, and transform all input to your desired character set if there's a mismatch. This will rarely if ever trip up a legitimate request.
tl;dr: Not a concern unless you're using a really old MySQL version and/or aren't making sure your data is in a known-good character set. Always use database-specific escape mechanisms for maximum safetey, and always assume the user is out to get you.
magic_quotes_gpc
We all hate it and many servers still use this setting and knowingly enough some provides will argue it's safer but I must disagree.
The question I have is, what would a backslash be needed for?
I want to remove them completely which I can do but I am not sure if they are needed?
EDIT
Other then SQL injection.
magic_quotes_gpc() was provided based on the misguided notion that ALL data submitted to PHP from any external source would be immediately inserted into a database. If you wanted to send that data somewhere OTHER than a database, you had to remove the slashes that PHP just inserted, doubling the work required.
As well, not all databases use slashes for escaping metacharacters. \' is fine in MySQL, but in MS Access, escaping a single quote is actually '' - so not only was PHP doing unecessary work, in many situations, it was doing the work WRONG to begin with.
And then, on top of all that, addslashes (which is basically what magic_quotes_gpc() was calling internally) can't handle all forms of SQL injection attacks, particularly where Unicode is used. addslashes is a glorified form of str_replace("'", "\\'", $string), which works at the ASCII level - plenty of Unicode sequences can look like regular ascii, but get turned into SQL metacharacters after a simplistic addslashes() has wreaked its havoc.
They are for preventing SQL injection exploits, a very serious issue you should read up on if you're going to be coding for the web.
You should look into prepared queries, which is a much better way of avoiding SQL injection.
There is no good reason to have this feature in PHP.
That's why it's officially deprecated and will not exist in future versions.
If there were good reasons to keep it, the developer community would have done so.
They are designed to make us do extra work to remove them. For example, some code in Dokuwiki.
Don't forget about magic_quotes_runtime too.