what are the best practices to prevent sql injections - php

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.

Related

Sanitize POST htmlentities or plus stripslashes and strip_tags

As always I start this saying that I am learning.
I saw in several books and even here, that a lot of user when we are talking about sanitize, for example, Form>Input>Submit, they use
function sanitizeexample($param)
{
$param = stripslashes($param);
$param = strip_tags($param);
$param = htmlentities($param);
return $param;
}
$name = sanitizeexample($_POST['name']);
Instead of JUST:
function sanitizeexample($param)
{
$param = htmlentities($param);
return $param;
}
$name = sanitizeexample($_POST['name']);
So here the question. Do stripslashes() and strip_tags() provide something else regarding to security? Or it´s enough with htmlentities().
And I´m asking JUST to know which is the best to use.
Whether strip_tags() provides a value-add is dependent on your particular use case. If you htmlentities() a string that contains html tags, you're going to get the raw html content escaped and rendered on the page. The example you give is probably making the assumption that this is not what you want, and so by doing strip_tags() first, html tags are removed.
stripslashes is the inverse to addslashes. In modern (PHP >= 5.4) PHP code, this is not necessary. On legacy systems, with magic_quotes_gpc enabled, user input from request variables are automagically escaped with addslashes so as to make them "safe" for direct use in database queries. This has widely been considered a Bad Idea (because it's not actually safe, for many reasons) and magic_quotes has been removed. Accordingly, you would now not normally need to stripslashes() user input. (Whether you actually need to is going to be dependent on PHP version and ini settings.)
(Note that you would still need to properly escape any content going into your database, but that is better done with parameterized queries or database-specific escaping functions, both of which are outside the scope of this question.)
It depends on your goals:
if you're getting user's data passed from html form - you should
definitely apply strip_tags(trim($_POST['name'])) approach to
sanitize possible insecure and excessive data.
if you are receiving uploaded user's file content and need to save
content formatting - you have to consider how to safely process and
store such files making some specific(selective) sanitizing

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.

Is this safe enough for SQL injection

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.

SQL Injection, Quotes and PHP

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.

The ultimate clean/secure function

I have a lot of user inputs from $_GET and $_POST... At the moment I always write mysql_real_escape_string($_GET['var'])..
I would like to know whether you could make a function that secures, escapes and cleans the $_GET/$_POST arrays right away, so you won't have to deal with it each time you are working with user inputs and such.
I was thinking of an function, e.g cleanMe($input), and inside it, it should do mysql_real_escape_string, htmlspecialchars, strip_tags, stripslashes (I think that would be all to make it clean & secure) and then return the $input.
So is this possible? Making a function that works for all $_GET and $_POST, so you would do only this:
$_GET = cleanMe($_GET);
$_POST = cleanMe($_POST);
So in your code later, when you work with e.g $_GET['blabla'] or $_POST['haha'] , they are secured, stripped and so on?
Tried myself a little:
function cleanMe($input) {
$input = mysql_real_escape_string($input);
$input = htmlspecialchars($input, ENT_IGNORE, 'utf-8');
$input = strip_tags($input);
$input = stripslashes($input);
return $input;
}
The idea of a generic sanitation function is a broken concept.
There is one right sanitation method for every purpose. Running them all indiscriminately on a string will often break it - escaping a piece of HTML code for a SQL query will break it for use in a web page, and vice versa. Sanitation should be applied right before using the data:
before running a database query. The right sanitation method depends on the library you use; they are listed in How can I prevent SQL injection in PHP?
htmlspecialchars() for safe HTML output
preg_quote() for use in a regular expression
escapeshellarg() / escapeshellcmd() for use in an external command
etc. etc.
Using a "one size fits all" sanitation function is like using five kinds of highly toxic insecticide on a plant that can by definition only contain one kind of bug - only to find out that your plants are infested by a sixth kind, on which none of the insecticides work.
Always use that one right method, ideally straight before passing the data to the function. Never mix methods unless you need to.
There is no point in simply passing the input through all these functions. All these functions have different meanings. Data doesn't get "cleaner" by calling more escape-functions.
If you want to store user input in MySQL you need to use only mysql_real_escape_string. It is then fully escaped to store safely in the database.
EDIT
Also note the problems that arise with using the other functions. If the client sends for instance a username to the server, and the username contains an ampersand (&), you don;t want to have called htmlentities before storing it in the database because then the username in the database will contain &.
You're looking for filter_input_array().
However, I suggest only using that for business-style validation/sanitisation and not SQL input filtering.
For protection against SQL injection, use parametrised queries with mysqli or PDO.
The problem is, something clean or secure for one use, won't be for another : cleaning for part of a path, for part of a mysql query, for html output (as html, or in javascript or in an input's value), for xml may require different things which contradicts.
But, some global things can be done.
Try to use filter_input to get your user's input. And use prepared statements for your SQL queries.
Although, instead of a do-it-all function, you can create some class which manages your inputs. Something like that :
class inputManager{
static function toHTML($field){
$data = filter_input(INPUT_GET, $field, FILTER_SANITIZE_SPECIAL_CHARS);
return $data;
}
static function toSQL($field, $dbType = 'mysql'){
$data = filter_input(INPUT_GET, $field);
if($dbType == 'mysql'){
return mysql_real_escape_string($data);
}
}
}
With this kind of things, if you see any $_POST, $GET, $_REQUEST or $_COOKIE in your code, you know you have to change it. And if one day you have to change how you filter your inputs, just change the class you've made.
May I suggest to install "mod_security" if you're using apache and have full access to server?!
It did solve most of my problems. However don't rely in just one or two solutions, always write secure code ;)
UPDATE
Found this PHP IDS (http://php-ids.org/); seems nice :)

Categories