Quick one.
I'm in the process of migrating an old web application that uses mysql to mysqli. I used to protect against SQL injection with a custom sanitation function I wrote:
function sani($text=""){
if (!is_array($text)) {
$text = str_replace("<", "<", $text);
$text = str_replace(">", ">", $text);
$text = str_replace("\"", """, $text);
$text = str_replace("'", "'", $text);
return $text;
}
}
They way I used to use this:
mysql_query("SELECT * FROM `table` WHERE `username` = '" . $sani($userinput) . "'");
Basically all it does is change symbols that can be used for injection into html encoding. It has worked fine up until now, but since i'm migrating to mysqli, I wanted to know if prepared statements would be more secure than this function.
Also, I have read a lot about the speed differences between prepared and unprepared statements, is it really that noticeable? I do around a hundred queries a second, so I doubt I would be affected very much?
Thanks!
Yes, prepared statements would certainly be more secure than this function, and they have the added benefit of not having to decode your data when you get it back from your database, too. By the way, even for the old mysql library, you really should rely on mysql_real_escape_string rather than your custom-built sanitation function :)
Prepared statements can be much faster than unprepared statements, and in a typical usage situation, you'll benefit from this even if you're "just" doing 100 queries/second.
The security of prepared statements originate in fact, that in prepared statements the query and data are sent separately. This is why it becomes impossible to perform 1st type of SQL injection (the 2nd type (or indirect) injections are ones that are caused if the query is concatenated from data in database).
The problem with your "protective function" is that it does not cover all cases. If you care to learn more about the issue, you could read slide from Slides From Recent Presentations on SQL Injection or slides on SQL injection: Not only AND 1=1.
Do not do that, ever.
Save the data as is using prepared statements so you always have the original data in your database even if it contains malicious code, no problem. In fact, you want that to see how they tried to hack you, or something.
Just create a simple filter when you output the unsafe (user submitted) data to replace the unwanted chars, such as < > etc.
Related
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'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.
I'm using mysqli prepared statements. Should I still sanitise the user input with some function like:
function sanitise($string){
$string = strip_tags($string); // Remove HTML
$string = htmlspecialchars($string); // Convert characters
$string = trim(rtrim(ltrim($string))); // Remove spaces
$string = mysql_real_escape_string($string); // Prevent SQL Injection
return $string;
}
Thanks.
No! No and no. If you are already using prepared statements, MySQL needs to see the value, not some escaped version of it. If you add mysql_real_escape_string to a string and make that the value for a prepared statement, you have just junked it, for example, quotes get doubled up!
Now, as for sanitising data-wise, that's entirely up to the business rules as to what is or is not valid input. In your example, strip_tags is more about html->raw (format) conversion than sanitation. So is rtrim(ltrim - this is a business transformation.
Yes. When using prepared statements you are safe from mysql injections, but still there could be special characters, strip tags or spaces, so those you will still need to take care of those.
See PHP: Is mysql_real_escape_string sufficient for cleaning user input?
UPDATE:
You are safe from mysql injections so you should not use real_mysql_scape_string or scape any quotes.
Prepared statements are there to keep your query form being subverted by malicious input. But there's plenty of malicious content that is perfectly acceptable in an SQL query, but will attack a browser when redisplayed later.
Doing mysql_real_escape_string on data going into a prepared statement is generally redundant (there are exceptions, but they're special-ish cases).
Here is an Object orientated solution to your question:
public function sanitize($var){
if(is_array($var))
foreach($var as $k => $v) $var[$k] = $this->db->real_escape_string($v);
else
$var = $this->db->real_escape_string($var);
return $var;
}
You should always sanitize your user inputs before submitting them to the database. I would just stick with mysql_real_escape_string as the others are not that much necessary unless you are putting them back on the URL.
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.
I'm working on a site where contents pages are handled with mod_rewrite and I'm trying to make the URL managed with mod_rewrite protected from SQL injections with some char restriction, because users can create pages contents like this:
http://site.com/content-type/Page-created-by-user
My doubts come when they insert something like:
http://site.com/architect/Giovanni+Dall'Agata
I need to insert ' char because I can have names like this for example of famous architects, but I don't know if I can keep data safe and how prevent SQL injections with this character.
Should I do something particular to prevent attacks?
I'm using PDO class in PHP like this:
$architect = strip_tags (trim ($_REQUEST["architect"]));
// pdo class etc..
$pdo_stmt->bindParam (":arch", $architect, PDO::PARAM_STR);
// and the other code here...
Users can't create pages with these chars: < > / \ * ? = should I ban ' and " too?
Or should I permit only one of ' and " chars or can I use them together and keep server safe?
$stmt->bindParam (and bindValue, and in general, prepared statements) are safe against SQL injection. All serious SB frameworks support a way of adding parameters to a query, and values added that way are sanitized. You should always do that and never insert variables data coming from users (see comments) manually into an SQL query string.
That still leaves the question of XSS injections, which are easier to miss (though also less dangerous); to avoid them, make sure you always use htmlspecialchars($var,ENT_QUOTES) (or urlencode, depending on the context).
PDO automatically escapes characters like ' so you should be ok, just make sure you have register_globals and magic_quotes turned off and always use bindParam for your queries.
Also if your talking about creating dynamic URL's you shouldn't have the ' character in them anyways. I always use:
$str = preg_replace("([^0-9a-zA-Z\-])", "", $str);
Which removes anything thats not 0-9, a-z or a dash from the string.