I'm quite confused now and would like to know, if you could clear things up for me.
After the lateste Anon/Lulsec attacks, i was questioning my php/mysql security.
So, i thought, how could I protect both, PHP and Mysql.
Question: Could anyone explain me, what's best practice to handle PHP and Mysql when it comes to quotes?
Especially in forms, I would need some kind of htmlspecialchars in order to protect the html, correct?
Can PHP be exploitet at all with a form? Is there any kind of protection needed?
Should I use real_escape_string just before a query? Would it be wrong/bad to use it already within PHP (see sanitize_post function)?
Currently i'm using the following function. The function "sanitizes" all $_POST and $_GET variables. Is this "safe"?
function sanitize_post($array) {
global $db;
if(is_array($array)) {
foreach($array as $key=>$value) {
if(is_array($array[$key])) {
$array[$key] = sanitize_post($array[$key]);
} elseif(is_string($array[$key])) {
$array[$key] = $db->real_escape_string(strtr(stripslashes(trim($array[$key])), array("'" => '', '"' => '')));
}
}
} elseif(is_string($array)) {
$array = $db->real_escape_string(strtr(stripslashes(trim($array)), array("'" => '', '"' => '')));
}
return $array;
}
I'm using PHP 5.3.5 with Mysql 5.1.54.
Thanks.
mysql_real_escape_string deserves your attention.
However direct queries are a quagmire and no longer considered safe practice. You should read up on PDO prepared statements and binding parameters which has a side benefit of quoting, escaping, etc. built-in.
BEST practice is always to use prepared statements. This makes SQL injection impossible. This is done with either PDO or mysqli. Forget about all the mysql_* functions. They are old and obsolete.
Question: Could anyone explain me, what's best practice to handle PHP
and Mysql when it comes to quotes?
That's easy: Use prepared statements, e. g. with PDO::prepare or mysqli_prepare.
There is nothing like "universal sanitization". Let's call it just quoting, because that's what its all about.
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 (always 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()
Don't waste the effort using mysql_real_escape_string() or anything like that. Just use prepared statements with PDO and SQL injection is impossible.
I usually use the PHP functions stripslashes and strip_tags on the variables as they come in via $_POST (or $_GET, depending on what you use) and mysql_real_escape_string during the query. (I'm not sure if this is "right" but it's worked for me so far.) You can also use PHP's built in validate filters to check things like email addresses, url's, data types, etc. PDO is supposedly decent at preventing SQL injection but I haven't had any experience with it yet.
The basic workflow should be
$data = $_POST['somefield which will go into the database'];
... do data validation ...
if (everything ok) {
$escaped_data = escape_function($data);
$sql = " ... query here with $escaped_data ... ";
do_query($sql);
}
Basically, data that's been escaped for database insertion should ONLY be used for database insertion. There's no point in pre-processing everything and overwriting all data with db-escaped values, when only 2 or 3 of 50(say) values actually go anywhere near the db.
Ditto for htmlspecialchars. Don't send data through htmlspecialchars unless it's headed for an HTML-type display.
Don't store data in the DB formatted for one particular purpose, because if you ever need the data in a different form for some other purpose, you have to undo the escaping. Always store raw/unformatted data in the db. And note: the escaping done with mysql_real_escape_string() and company does not actually get stored in the db. It's there only to make sure the data gets into the database SAFELY. What's actually stored in the db is the raw unescaped/unquoted data. Once it's in the database, it's "safe".
e.g. consider the escaping functions as handcuffs on a prisoner being transferred. While the prisoner is inside either jail, cuffs are not needed.
Related
I've been working on a code that escapes your posts if they are strings before you enter them in DB, is it an good idea? Here is the code: (Updated to numeric)
static function securePosts(){
$posts = array();
foreach($_POST as $key => $val){
if(!is_numeric($val)){
if(is_string($val)){
if(get_magic_quotes_gpc())
$val = stripslashes($val);
$posts[$key] = mysql_real_escape_string($val);
}
}else
$posts[$key] = $val;
}
return $posts;
}
Then in an other file:
if(isset($_POST)){
$post = ChangeHandler::securePosts();
if(isset($post['user'])){
AddUserToDbOrWhatEver($post['user']);
}
}
Is this good or will it have bad effects when escaping before even entering it in the function (addtodborwhater)
When working with user-input, one should distinguish between validation and escaping.
Validation
There you test the content of the user-input. If you expect a number, you check if this is really a numerical input. Validation can be done as early as possible. If the validation fails, you can reject it immediately and return with an error message.
Escaping
Here you bring the user-input into a form, that can not damage a given target system. Escaping should be done as late as possible and only for the given system. If you want to store the user-input into a database, you would use a function like mysqli_real_escape_string() or a parameterized PDO query. Later if you want to output it on an HTML page you would use htmlspecialchars().
It's not a good idea to preventive escape the user-input, or to escape it for several target systems. Each escaping can corrupt the original value for other target systems, you can loose information this way.
P.S.
As YourCommonSense correctly pointed out, it is not always enough to use escape functions to be safe, but that does not mean that you should not use them. Often the character encoding is a pitfall for security efforts, and it is a good habit to declare the character encoding explicitely. In the case of mysqli this can be done with $db->set_charset('utf8'); and for HTML pages it helps to declare the charset with a meta tag.
It is ALWAYS a good idea to escape user input BEFORE inserting anything in database. However, you should also try to convert values, that you expect to be a number to integers (signed or unsigned). Or better - you should use prepared SQL statements. There is a lot of info of the latter here and on PHP docs.
Currently I use this strategy:
After submitting a HTML form or data sent using jQuery's $.get() or $.post() I need to know what is come and then apply logic on the basis of that.
suppose, I've got $_POST['username'], $_POST['password'] and $_POST['login_submit_button'].
In my processing script file, I do like this:
if(isset($_POST['login_submit_button']) && isset($_POST['username']) && $_POST['username'] != "" && isset($_POST['password']) && $_POST['password'] != "") {
// calling a common function safe_vars() which does
// mysql_real_escape_string(stripslashes(trim($any_variable_need_to_become_safe)))
// and now using the above variables for different purposes like
// calculation, insertion/updating old values in database etc.
}
I know all this logic is wrong or having serious issues, so I want a much-secure and perfect solution instead of this. I welcome to find out vulnerabilities and severe security-bleaches in my strategy. This question can help others too, if answers came more explanatory, this can be informative community wiki.
There is no way to make a generic super "make things safe" function.
mysql_real_escape_string
You shouldn't use this at all. It uses the old mysql API, and assumes you are going to be manually smashing strings together to make SQL. Don't do that. Use PDO or mysqli and a function that deals in prepared queries and bound arguments.
stripslashes
This is an antidote to magic quotes. If magic quotes are not on it will destroy data. Don't use it. Turn magic quotes off instead.
trim
This destroys data. Don't use it unless you really want to remove white space at the start and end of the string.
Escape data for the target language immediately before inserting data into that language.
For SQL, use bound arguments and prepared queries.
For HTML, use htmlspecialchars or a template language that does escaping for you, such as mustache.
Alternatively, (if you want to allow HTML) parse it, generate a DOM, filter it using a whitelist, then serialise it back to HTML.
For JSON, use encode_json
etc.
You only need to stripslashes if you have magic_quotes enabled (use get_magic_quotes_gpc to check)
You should white list filter your POST vars using filter_var or ctype_* or preg_match (as well as checking bound conditions such as length and presence)
Use prepared statements / PDO for your queries to ensure proper escaping
Escape any html output with htmlentities
Nothing is bullet proof, however the above are good practices to avoid SQL injection / XSS.
I just read about SQL injection and found this function on the blog i was reading
I am wondering if it is safe for SQL injection.. say if i pass do remove_mq($_POST) to it, could i be using $_POST["var"] inside a query without a problem?
function remove_mq($array){
foreach($array as $key => $value){
if(is_array($value)){
$array[$key] = remove_mq($value);
}
else{
$array[$key] = addslashes($value);
}
}
return $array;
}
No. Addslashes is not the proper function to escape for a query. You need to use mysql_real_escape_string
Besides that, you should not perform SQL escaping before actually using a value in a query. Assume you have something like <input name="foo" value="$_POST[foo]" - then you need it htmlspecialchars()-escaped and not addslashes(etc.)-escaped
Besides that, the best solution would be using PDO with prepared statements since you separate SQL queries from params so you do not need any escaping at all.
Best practice nowadays is prepared queries.
Example:
$stmt = $pdo->prepare('SELECT username FROM users WHERE id = :id');
$stmt->execute(array(':id' => $_GET['id']));
$result = $stmt->fetchAll();
This code is totally secure
Well, all the answers above missing the point.
addslashes() is good enough as long as your data encoding is either utf-8 or any single-byte one.
but the whole approach, despite of the function used, is utterly wrong
Doing massive escaping over $_POST makes no sense and doesn't guarantee full protection.
No addslashes nor mysql_real_escape_string do any actual protection. It does as little as string delimiters escaping. So, you have to use this function:
only on strings
only on strings actually going into query.
all other required manipulations are described in the link in the comments
and some info for ones who feel extremely educated in the matter:
your beloved PDO, when used out of the box, do use the same defamed addslashes
Nope - just use the built in function for it! mysql_real_escape_string() is always going to be more reliable than any function you can write yourself.
Here's an article explaining why you should use mysql_real_escape_string over addslashes.
Of course, the best approach is to use prepared queries. Here's some information on them.
I have done some research and still confused, This is my outcome of that research. Can someone please comment and advise to how I can make these better or if there is a rock solid implementation already out there I can use?
Method 1:
array_map('trim', $_GET);
array_map('stripslashes', $_GET);
array_map('mysql_real_escape_string', $_GET);
Method 2:
function filter($data) {
$data = trim(htmlentities(strip_tags($data)));
if (get_magic_quotes_gpc())
$data = stripslashes($data);
$data = mysql_real_escape_string($data);
return $data;
}
foreach($_GET as $key => $value) {
$data[$key] = filter($value);
}
Both methods you show are not recommendable
Blanket "sanitizing" data is counter-productive, because data needs to be sanitised in different ways depending on how it is going to be used: Using it in a database query needs different sanitation from outputting it in HTML, or from using it as parameters in a command line call, etc. etc.
The best way to do sanitation is immediately before the data is being used. That way, it is easy for the programmer to see whether all data is actually getting sanitized.
If you use the mysql_* family of functions, do a mysql_real_escape_string() on every argument you use in a query:
$safe_name = mysql_real_escape_string($_POST["name"]);
$safe_address = mysql_real_escape_string($_POST["address"]);
$result = mysql_query ("INSERT INTO table VALUES '$safe_name', '$safe_address'");
If you use the PDO or mysqli families of functions, you can make use of parametrized queries, which eliminate most of the SQL injection woes - all everyday ones at any rate.
It is perfectly possible to write safe queries with mysql_*, and it is also perfectly possible to introduce a security hole in a PDO query, but if you are starting from scratch, consider using PDO or mysqli straight away.
Use strip_tags() only if you are planning to output user entered data in HTML; note that you need to do an additional htmlspecialchars() to reliably prevent XSS attacks.
The only blanket sanitation method that has some merit is the
if (get_magic_quotes_gpc())
$data = stripslashes($data);
call which filters out the layer of escaping added by the now-deprecated "magic quotes" feature of earlier versions of PHP.
I think this is enough (EDIT: with $data we mean here e.g. one text field from a form, $_POST['example'] etc):
if (get_magic_quotes_gpc())
$data = stripslashes($data);
$data = mysql_real_escape_string($data);
I usually do the first when receiving the $_POST or $_GET data (before input testing) and the latter right before - or during - composing of the sql query.
Additionaly trim it, if you want (maybe you don't always want that). However the best solution would be using some libraries for working with database.
You can either:
Escape all user input supposed for the DB using mysql_real_escape_string (or the mysqli_variant)
Use prepared statements (with mysqli_ or PDO)
Note that you should turn off magic_quotes if possible. If it's on, just use stripslashes (like in your example) as it's deprecated and you should not rely on it.
Also note that you should not do this for data which is not supposed to be inserted into the database as it's completely useless.
I like type-casting whenever you're dealing with ints;
e.g.
$id=(int)$_GET['id'];
if($id<1){//I generally don't pass around 0 or negative numbers
//Injection Attempt
die('Go Away');
}
//Continue with the number I **know** is a number
Well, among your codes only mysql_real_escape_string() function is SQL injection related. However, being completely misplaced.
And it's not enough.
In fact, this function should be used with quoted strings only.
And it's useless for the everything else. So, in any other case other precautions should be taken.
For the complete explanation see my earlier answer: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
All other functions you're using have no relation to SQL at all.
Note that using both strip_tags and htmlentities is redundant. and htmlentities is obsolete.
I'd use only htmlspecialchars() instead of these two, and not before inserting into database but right before sending it to browser.
if(get_magic_quotes_gpc())
{
$location_name = trim(mysql_real_escape_string(trim(stripslashes($_GET['location_name']))));
}
else
{
$location_name = trim(mysql_real_escape_string(trim($_GET['location_name'])));
}
That's the code I have so far. seems to me this code is fundamentally ... OK. Do you think I can safely remove the inner trim(). Please try not a spam me with endless version of this, I want to try to learn how to do this better.
UPDATE
So after reading some of the responses, I think I have come to understand a good method for safely getting data from a user, storing it and then displaying it back.
When you first load the page
$foo = trim($_GET['foo']);
if(get_magic_quotes_gpc())
{
$foo = stripslashes($foo);
}
Then when you come to use this variable as part of a SQL string, even if not storing the data in the database, you should escape it.
mysql_real_escape_string($foo);
And finally, if reading data from the database and wanting to display it as HTML, such a post on a blog or forum, you should pass the variable using htmlspecialchars
echo(htmlspecialchars($bar));
Would any one like to suggest a better set of functions to use? other then obviously wrapping these functions to make them simpler to call.
Here:
$location_name = trim($_GET['location_name']);
if(get_magic_quotes_gpc()) $location_name=stripslashes($location_name);
Then there is also the SQL injection protection, but don't do this until the very last moment before sticking this var in an SQL query. And even then don't apply the changes to the var itself, but rather a copy. You might want to show $location_name to the user afterwards (for example if the form fails). So
$sql="UPDATE whatever(location) VALUES('" . mysql_real_escape_string($location_name) . "')"
I'm assuming of course that $location_name will end up in the database; otherwise you don't need mysql_real_escape_string.
Finally you want to use htmlspecialchars if you're going to display $location_name on your page somewhere.
Edit: You want to use htmlspecialchars() just before displaying the data (definately don't save data that has already been transformed via htmlspecialchars in your database). In general you want to use escaping functions at the last moment and then on a copy of your var. That way you know that at any point during the script the var is the original one and is not carrying some random escape characters from a transformation that happened somewhere before.
You also know where your escape functions are/should be. sql escaping is near/at your sql query. XSS escaping (htmlspecialchars) is near the part where you display data in a web page.
Finally once you get the grip of things, you could always forego SQL escaping by using PHP's PDO functions. Also in the future you might want to take at look at this: Do htmlspecialchars and mysql_real_escape_string keep my PHP code safe from injection?
I am sorry to say but everything in your question is wrong.
First, it has nothing to do with performance, by any means. these functions never become a bottleneck and never cause any performance issue.
Next, You've choose wrong place to get rid of magic quotes. Magic quotes is input data related, not database related. It it is better to make a distinct function and place it in your configuration file, being included into every script. You can use one from here
So, the code become like this:
$location_name = mysql_real_escape_string(trim($_GET['location_name']));
But i strongly advise you not to mix database escaping with anything else, as anything else is optional while database escaping is strict and unconditional.