PHP : Which is more efficient in sanitizing $_get variable? - php

The string $title is expected only to be lowercase alpha-numeric or the symbol "-" .
In this situation which of the following methods is efficient defense against security vulnerability?
$title=$_GET["title"];
$title = strtolower(preg_replace("/[^a-z0-9\-]+/i", "-", $title));
< mysql query using $title goes here >
OR
$title=$_GET["title"];
$title = mysql_real_escape_string($title);
< mysql query using $title goes here >

You should do both.
$title = strtolower(preg_replace("/[^a-z0-9\-]+/i", "-", $title));
And then $title = mysql_real_escape_string($title);
It's always a good practice to escape your mysql values, in this case it's not useful to escape it but if in the future the rule for the title changes and you can put any character, maybe you won't remember to change it

Never use your own escaping methods to escape SQL queries; the database servers will do much better job with it.
To answer the question: regexps are generally really slow. I suppose the function call will be much faster.
Also, never rely to input by users.

I would use the mysql_real_escape_string, this will remove most of the mysql injection problems. If the title does not exist (because, for example, somebody is trying to do mysql injection) then no rows will be found and thus, you display a 404 error

A combination of both. You have to remember your regular expression may not be extensive.

You do not sanitize input!
You validate input (i.e. accept it or decline it) and sanitize output (i.e. change it's representation) using a method appropriate to the destination of the data.
Hence:
if (preg_match("/^[a-z0-9\-]+$/i", $_GET['title'])) {
$my_title=mysql_real_escape_string(strtolower($_GET['title']));
$sql=....'$my_title'....
} else {
print "Invalid value for title";
exit;
}

Related

Function escaping before inserting in mysql

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.

I use mysql_real_escape_string before SQL INSERT, but then have to apply stripslashes to my retrieved data. Is it normal?

I'm no PHP/SQL expert, and I've juste discovered that i had to apply mysql_real_escape_string to secure my SQL INSERTS.
I made a function using several advice found on the net, here it is:
function secure($string)
{
if(is_numeric($string))
{ $string = intval($string); }
elseif (is_array($string))
{
foreach ($string as $key => $value) {
$string[$key] = secure($value);
}
}
else if ($string === null)
{
$string = 'NULL';
}
elseif (is_bool($string))
{
$string = $string ? 1 : 0;
}
else
{
if (get_magic_quotes_gpc()) { $value = stripslashes($string); }
$string = mysql_real_escape_string($string);
$string = addcslashes($string, '%_');
}
return $string;
}
Thing is, when I have a look at my tables content, it contains backslashes.
And then logically, when I retrieve data I have to apply stripslashes to it to remove these backslashes.
Magic Quotes are off.
QUESTION 1)
Now I think that even though I use mysql_real_escape_string to secure my data before SQL insertion, backslashes should not appear in my content ? Can you confirm this ?
QUESTION 2)
If not normal, why are these backslashes appearing in my phpMyAdmin content and retrievals ? What did I did wrong ?
QUESTION 3)
A guess I have is that mysql_real_escape_string could be applied twice, isn't it ?
If so, what could be a function to prevent mysql_real_escape_string being applied many times to a same string, leading to many \\ to a same escapable character ?
Thanks a lot by advance for your inputs guys !
oh, what a senseless function. I know it's not your fault but ones who wrote it in their stupid articles and answers.
Get rid of it and use only mysql_real_escape_string to escape strings.
you have mixed up everything.
first, no magic quotes stuff should be present in the database escaping function.
if you want to get rid of magic quotes, do it centralized, at the very top of ALL your scripts, no matter if they deal with the database or not.
most of checks in this function are useless. is_bool for example. PHP will convert it the same way, no need to write any code for this.
LIKE related escaping is TOTALLY distinct matter, and has nothing to do with safety.
is numeric check is completely useless, as it will help nothing.
Also note that escaping strings has nothing to do with security.
I's just a syntax rule - all strings should be escaped. No matter of it's origin or any other stuff. Just a strict rule: every time you place a string into query, it should be quoted and escaped. (And of course, if you only escape it but not quote, it will help nothing)
And only when we talk of the other parts of query, it comes to the SQL injection issue. To learn complete guide on this matter, refer to 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?
Your stripslashed $string is stored to the wrong variable $value instead of $string:
if (get_magic_quotes_gpc()) { $value = stripslashes($string); }
should be
if (get_magic_quotes_gpc()) { $string = stripslashes($string); }
Are you sure you aren't calling mysql_real_escape_string more than once, each time you call it with escapable characters you will end up adding more and more slashes. You want to call it only once. Also, why are you also calling addcslashes? mysql_real_escape_string should be enough. If you call it only once, you should never have to call stripslashes on the data after retrieving it from the database.
You can't really tell if mysql_real_escape_string is applied more than once, I'd suggest going back and re-reading your code carefully, try debug printing the values just before they are inserted into the db to see if they are look 'over-slashed'.
Btw, if you are using prepared statements (e.g. via mysqli) you dont need to escape your strings, the DB engine does this for you, this could be the problem too.
Remove addslashes completely from all of your code. This is the leading cause for slashes being inserted into database.
function escape($string) {
if (get_magic_quotes_gpc()) {
$string = stripslashes($string);
}
return mysql_real_escape_string($string);
}
Always check if magic_quotes_gpc is enabled, if it is perform stripslashes and escape the data.
Escaped = "don\'t use addslashes"
When it goes into database the '\' is removed.

What are ways to improve my PHP data sanitation class?

I'm putting together a site, (we're already using javascript to prevalidate on the client side). However after getting tired of writing mysql_real_escape_string every other line. I wrote this class that only has two functions mainly focused on sanitising data in user-input/sql. My question is, what are ways to achieve easier input-sanitizing and while improving code readability?
<?php
class Safe {
function userinput($string){
$string = strip_tags($string);
$string = htmlspecialchars($string);
return $string;
}
function sql ($string){
$sqlstuff = Array("union", "select", "update", "delete", "outfile", "create");
$string = Safe::str($string);
$string = mysql_escape_string($string);
$string = str_ireplace($sqlstuff, "", $string);
return $string;
}
}
?>
Sorry, this is going to sound harsh, but your class is completely broken.
You should not be using htmlspecialchars for sanitizing input, it is only useful for escaping output. You do not need to encode HTML for insertion to the database nor should you. Only using htmlspecialchars when sending output to the browser
You should not be stripping tags from your input, you should be leaving them alone and again using htmlspecialchars when you output that data later to insure HTML tags are escaped and not interpreted by the browser
You should not be using mysql_escape_string or mysql_real_escape_string, you should be using PDO. If you are writing a new site there is absolutely no reason not to start out correctly and use PDO. Do it.
You should not be filtering out "union", "select", etc, that's dumb. Those words can appear in regular English language, and they're harmless if you're properly escaping quotes which PDO will handle for you.
Again, sorry for the harsh tone of this answer, but scrap the entire thing and use PDO. There is literally nothing salvageable here.
It's a good idea to use a class like that, particularily if it simplifies input handling. There's however a few points I'd like to comment on:
You should use mysql_real_escape_string instead of the PHP3 mysql_escape_string.
The first function should be called html or something. userinput sounds to vague and misrepresentative.
HTML escaping needs more parameters htmlspecialchars($str, ENT_QUOTES, "UTF-8") to be perfectly safe
The blacklisting of dangerous SQL keywords is not a good idea. It hints at a wrong approach to using SQL queries (if you receive queries via HTTP requests, that's your problem).
Also you should not attempt to filter them. Instead detected them, write to the error/security log, and die() immediately. If there is an attempt to circumvent security, there's no point in attempting any "cleaning" of the request.
You can also use filter_* functions that are bundled with PHP and provide you with the mechanism to filter request parameters according to specific filtering rules.
With few extra tricks, you could even filter arrays of different types of data (thanks to erisco!).
class sanitizer {
public function sanitizeValues($values, $filters) {
$defaultOptions=FILTER_FLAG_NO_ENCODE_QUOTES | FILTER_FLAG_STRIP_LOW | FILTER_NULL_ON_FAILURE;
$filters=(array)$filters;
$values=(array)$values;
foreach ($filters as $key => $filter) {
if($parts=explode('/', $key)){
$v=&$values;
foreach ($parts as $part){
$v=&$v[$part];
}
$filter=(array)$filter;
$filter[1]=isset($filter[1])?$filter[1]:$defaultOptions;
$v=filter_var($v, $filter[0], $filter[1]);
// consider if you really need this here instead of PDO
// $v=mysql_real_escape_string($v);
}
else{
$values[$key]=isset($values[$key]) ? filter_var($values[$key], $filter[0], $filter[1]) : null;
}
}
return $values;
}
}
$manager=sanitizer::sanitizeValues($_GET['manager'], array(
'manager/managerID'=>FILTER_VALIDATE_INT,
'manager/username'=>FILTER_SANITIZE_STRING,
'manager/name'=>FILTER_SANITIZE_STRING,
'manager/email'=>FILTER_SANITIZE_STRING,
'manager/phone'=>FILTER_SANITIZE_STRING,
'manager/bio'=>FILTER_SANITIZE_STRING,
'manager/enabled'=>FILTER_VALIDATE_BOOLEAN,
'manager/password'=>FILTER_SANITIZE_STRING));
This will produce an array complete with all the needed fields based on the 'manager' parameter in _GET, with all values filtered and, optionally, escaped.

What would be a better way of doing the following

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.

How to santize user inputs in PHP?

Is this enough?
$listing = mysql_real_escape_string(htmlspecialchars($_POST['listing']));
Depends - if you are expecting text, it's just fine, although you shouldn't put the htmlspecialchars in input. Do it in output.
You might want to read this: What's the best method for sanitizing user input with PHP?
you can use php function : filter_var()
a good tutorial in the link :
http://www.phpro.org/tutorials/Filtering-Data-with-PHP.html
example to sanitize integer :
To sanitize an Integer is simple with the FILTER_SANITIZE_INT filter. This filter strips out all characters except for digits and . + -
It is simple to use and we no longer need to boggle our minds with regular expressions.
<?php
/*** an integer ***/
$int = "abc40def+;2";
/*** sanitize the integer ***/
echo filter_var($int, FILTER_SANITIZE_NUMBER_INT);
?>
The above code produces an output of 40+2 as the none INT values, as specified by the filter, have been removed
See:
Best way to stop SQL Injection in PHP
What are the best practices for avoid xss attacks in a PHP site
And sanitise data immediately before it is used in the context it needs to be made safe for. (e.g. don't run htmlspecialchars until you are about to output HTML, you might need the unedited data before then (such as if you ever decide to send content from the database by email)).
Yes. However, you shouldn't use htmlspecialchars on input. Only on output, when you print it.
This is because, it's not certain that the output will always be through html. It could be through a terminal, so it could confuse users if weird codes suddenly show up.
It depends on what you want to achieve. Your version prevents (probably) all SQL injections and strips out HTML (more exactly: Prevents it from being interpreted when sent to the browser). You could (and probably should) apply the htmlspecialchars() on output, not input. Maybe some time in the future you want to allow simple things like <b>.
But there's more to sanitizing, e.g. if you expect an Email Address you could verify that it's indeed an email address.
As has been said don't use htmlspecialchars on input only output. Another thing to take into consideration is ensuring the input is as expected. For instance if you're expecting a number use is_numeric() or if you're expecting a string to only be of a certain size or at least a certain size check for this. This way you can then alert users to any errors they have made in their input.
What if your listing variable is an array ?
You should sanitize this variable recursively.
Edit:
Actually, with this technique you can avoid SQL injections but you can't avoid XSS.
In order to sanitize "unreliable" string, i usually combine strip_tags and html_entity_decode.
This way, i avoid all code injection, even if characters are encoded in a Ł way.
$cleaned_string = strip_tags( html_entity_decode( $var, ENT_QUOTES, 'UTF-8' ) );
Then, you have to build a recursive function which call the previous functions and walks through multi-dimensional arrays.
In the end, when you want to use a variable into an SQL statement, you can use the DBMS-specific (or PDO's) escaping function.
$var_used_with_mysql = mysql_real_escape_string( $cleaned_string );
In addition to sanitizing the data you should also validate it. Like checking for numbers after you ask for an age. Or making sure that a email address is valid. Besides for the security benefit you can also notify your users about problems with their input.
I would assume it is almost impossible to make an SQL injection if the input is definitely a number or definitely an email address so there is an added level of safety.

Categories