This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 8 years ago.
I just had this idea of "preSecure" all userinput data from $_post and $_get. But I was wondering if this is good practice and would like some input on this. Here is what I came up with:
function clean_str($str){
return preg_replace('#[^a-z_ 0-9#.-]#i', '', $str);
}
if ($_POST){
foreach ($_POST AS $key => $val){
$_POST[$key] = clean_str($val);
}
}
if ($_GET){
foreach ($_GET AS $key => $val){
$_GET[$key] = clean_str($val);
}
}
This snippet would simply be run at the beginning of each http request. The clean_str function can be developed to allow other chars and replace characters etc (this is just an example). But I think the first goal are to simply prevent sql injection. The only bad thing I can see with this approach right now is if your "plugin" need to send sql commands from user input. The appraoch above could we wrapped in a function of course and be called if needed. Post and Get are global vars so that would not be a problem.
I'm actually writing my own framework (still a lot of work) that I will release if I ever be able to finish it. The thing is that I often see novice developers add $_POST['userinput'] inside database queries. The code above should make even that okay. Well that was some background of what I'm up to and why I bring this up.
I would be very happy to hear your thoughts. This might not be the best question for Stack Overflow, I suppose I want to open more like a discussion to this approach to share thoughts and inputs. But to formulate my question(s) to this, it would be something in line with: Are there any other approaches that would be faster or more scalable than this, or is equal to this, or can another function complement this approach? Is this approach good practice? Is it okay to overwrite the global vars of post and get data like above?
I know the code above is not object oriented, but it's about the approach of cleaning the users' input datas automatically before running checks on them. I think this will save a lot of code and headaches.
Please share your thoughts with me. As comments are limited here on Stack Overflow, I would appreciate if you reply as answers if you bring new thoughts to this table. Comments are to comment on specific thoughts/answers in this case.
First off if the user tries to inject an array then it will generate a notice, you should check for a string before calling it, else:
Notice: Array to string conversion
But to be honest, you are just creating your own version of magic quotes, which isn't a good idea.
The simple and better solution would be to use prepared statements, with PDO or MySQLi.
A post on the PHP Website for magic quote sums it up nicely:
The very reason magic quotes are deprecated is that a one-size-fits-all approach to escaping/quoting is wrongheaded and downright dangerous. Different types of content have different special chars and different ways of escaping them, and what works in one tends to have side effects elsewhere. Any sample code, here or anywhere else, that pretends to work like magic quotes --or does a similar conversion for HTML, SQL, or anything else for that matter -- is similarly wrongheaded and similarly dangerous.
Magic quotes are not for security. They never have been. It's a convenience thing -- they exist so a PHP noob can fumble along and eventually write some mysql queries that kinda work, without having to learn about escaping/quoting data properly. They prevent a few accidental syntax errors, as is their job. But they won't stop a malicious and semi-knowledgeable attacker from trashing the PHP noob's database. And that poor noob may never even know how or why his database is now gone, because magic quotes (or his spiffy "i'm gonna escape everything" function) gave him a false sense of security. He never had to learn how to really handle untrusted input.
Data should be escaped where you need it escaped, and for the domain in which it will be used. (mysql_real_escape_string -- NOT addslashes! -- for MySQL (and that's only unless you have a clue and use prepared statements), htmlentities or htmlspecialchars for HTML, etc.) Anything else is doomed to failure.
This is not a good approach. Preventing MySQL injection isn't just a matter of making sure certain characters are escaped -- there are still plenty of attacks you can do even if you try to sanitize this way. It seems like you're overcomplicating things, when you can use prepared statements and no longer worry about all the things you need to check for. http://www.php.net/manual/en/mysqli.prepare.php
Related
I have been working on this project for 7 years. Now dozens of people use this software every day in their work.
When I was beginning (with PHP 5.2.X), I didn't know that mysql_ would be deprecated, so I was using it freely. Now I am running PHP 5.4.4 and I feel the urgency of upgrading, but I have thousands of mysql_query calls in my code. It will take ages to migrate to PDO, but I know it must be finally done.
From the get-go I have been sanitizing user input going through each post, get, cookie, request array recursively like this:
$_POST = array_map_recursive('stripslashes', $_POST);
$_POST = array_map_recursive('mysql_real_escape_string', $_POST);
I prepend the file with this code with .htaccess, so each php file has it prepended. I know it is wrong, but I started coding knowing nothing about good practices and had never any reason/time to change that.
I also hold in array all (thousands) name/value pairs of inputs like select, radio, checkbox and I filter them every time.
Question: before I will be done changing each mysql_ function to PDO, what is the possible threat of mysql injection or any other mailicious attack on my code resulting from my above mentioned obsolete combination of mysql_ and global sanitization? Do you have any advice for this temporary solution for developers maintaining obsolete code with thousands of mysql_queries?
You're really in a pretty mess there. Your reliance on global escaping means all your values are being mangled as they come into your app. They're not the original values the user supplied anymore, they will have sprouted random new backslashes. However, as we all know, it's a terrible idea to do that, you should be using parameterised queries and bind your values.
Now here's your predicament: if you remove the global escaping and switch to binding values, all your mysql calls which relied on global escaping will be vulnerable to SQL injection. If you just leave in the global escaping and are additionally binding your data, it will contain random additional backslashes. stripslashes may remove them cleanly, or it may not. It's not designed to un-mysqlescape a string. Your only real way out of this mess is to replace all mysql function calls with prepared statements in one go; migrating little by little is almost impossible.
I have started using magic quotes and I have encountered a small problem, an easy problem I guess which I can't figure out.
I wan't to use striplashes which it does not remove when I write something in my textarea
Code:
<?php
echo "Removed Slashes: ";
// Remove those slashes
if(get_magic_quotes_gpc())
echo stripslashes($_POST['question']);
else
echo $_POST['question'];
?>
<form method='post'>
Question: <input type='text' name='question'/><br />
<input type='submit'>
</form>
I also tried this one which actually works!:
<?php
$str = "Is your name O\'reilly?";
// Outputs: Is your name O'reilly?
echo stripslashes($str);
?>
But now I want to use my input for the website for secutiry reasons
I cant put it any better than this comment by cHao on the manual page
The very reason magic quotes are deprecated is that a one-size-fits-all approach to escaping/quoting is wrongheaded and downright dangerous. Different types of content have different special chars and different ways of escaping them, and what works in one tends to have side effects elsewhere. Any sample code, here or anywhere else, that pretends to work like magic quotes --or does a similar conversion for HTML, SQL, or anything else for that matter -- is similarly wrongheaded and similarly dangerous.
Magic quotes are not for security. They never have been. It's a convenience thing -- they exist so a PHP noob can fumble along and eventually write some mysql queries that kinda work, without having to learn about escaping/quoting data properly. They prevent a few accidental syntax errors, as is their job. But they won't stop a malicious and semi-knowledgeable attacker from trashing the PHP noob's database. And that poor noob may never even know how or why his database is now gone, because magic quotes (or his spiffy "i'm gonna escape everything" function) gave him a false sense of security. He never had to learn how to really handle untrusted input.
Data should be escaped where you need it escaped, and for the domain in which it will be used. (mysql_real_escape_string -- NOT addslashes! -- for MySQL (and that's only unless you have a clue and use prepared statements), htmlentities or htmlspecialchars for HTML, etc.) Anything else is doomed to failure.
Really, take the advice. Don't use them, you won't learn anything useful by trying them out, just forget they ever existed.
Take a look at this article http://www.sitepoint.com/magic-quotes-headaches and this one http://econsultancy.com/us/blog/2663-web-app-security-basics-filtering-input-and-escaping-output for more information.
Well it's easy to attack but seemingly more effort to help. So here's my attempt at being helpful.
So, you are posting data back to the server and you want to ensure that whatever the user posts back does not end up somewhere that it can act maliciously.
The naive strategy you have adopted it to say ok, I'll sanitise data generically by turning on the magic quoting, which should escape all of the nasties...
However, it doesn't. Your job is to ensure that you use a custom sanitisation strategy when dealing with untrusted data, depending completely on how you use it. A good resource for learning where you should allow untrusted data to enter a particular location, and how to escape untrusted data can be found at OWASP
You'll notice that not all locations are suitable for untrusted data, escaped or otherwise. This highlights the fact that to truly implement secure websites you have to consider both where untrusted data is going and how data is getting there.
This question is more directly focussing on the how, because we are considering (and attacking the use of) a generic escaping mechanism. You are implying that turning on magic quotes is a suitable method for escaping data destined to all locations your untrusted data can end up.
Best practice says that actually, you need to use an escaping mechanism that is suitable for the location(s) you intend to use it. As has already been pointed out, the use of mysql_real_escape_string is a popular function which is specific to escaping strings for use in a MySQL query. People do use this mechanism, but the need to manually escape your data with this mechanism is superceded by the correct use of PHP Data Objects (PDO). (Binding your untrusted data to a parameter rather than manually building up query strings).
Other obvious escaping mechanisms include encoding html characters using htmlspecialchars or htmlentities and, the more generic quote escaping mechanisms addslashes and addcslashes. There are even escaping methods for command line arguments escapeshellarg and escapeshellcmd
So you can see that escaping data properly is far less trivial than applying magic quotes to all of your incoming data, and there are often well established mechanisms for escaping data safely depending on the location you intend to use it.
Easy fix to your problem is to create a simple function that you push all your data through if it is going to be touching your DB. Can be modeled as such:
function sanitizeString($var)
{
$var = strip_tags($var);
$var = htmlentities($var);
$var = stripslashes($var);
return mysql_real_escape_string($var);
}//end sanitizeString
I have a feedback form which will take a couple of user inputted fields along with a few fields generated by PHP functions like 'user-agent' and 'referer.'
My question is should these strings be sanitized before being inputted? I realize one could easily alter the user-agent and the referring page, but could it be possible for a visitor to add a SQL injection like string so when PHP pulls this info it potentially breaks my form?
For instance if a user changed their user-agent or referring page to include the string Robert'); DROP TABLE Students;--
The word "sanitize" is pretty ambiguous and and better to be avoided.
Speaking of a database interaction, there is no need to "sanitize" at all. Just use prepared statements.
What is even more important, the data source doesn't matter. It should never be a question, "should we properly handle the data from such and such source?". It's just illogical, if you think of it. Why making such a distinction? Why rely on such a vague judgement? Why not to have an established process that uniformly treats any data despite the source?
Not to mention it's just super simple to use prepared statements:
$stmt = $db->prepare("INSERT INTO log (user_agent, referrer) VALUES (?,?)");
$stmt->execute([$_SERVER['HTTP_USER_AGENT'],$_SERVER['HTTP_REFERER']]);
And it will not only make the code simpler yet secure, but also make it proof against human errors of all sorts.
Simple Answer: validate/sanitize/escape everything (like client-side data, for example) because everything could be modified and evil or contain unexpected characters that could break your query (like Col. Shrapnel explained).
to minimize risk you should also about using prepared statements instead of building SQL-strings on your own (Note: this doesn't mean you can leave out the checks).
First off all - I believe the best practice is to parametr-ise everything in the query including self generated values. For me it does not make the query (almost) bulletproof but it creates much nicer and readable queries.
When you use parameters and assign them later you use more explicit logic in your code and therefore it will function better in the long term.
Longer explanation can be found in the attached link:
How can I prevent SQL injection in PHP?
Always sanitize/filter any input from a browser.
Just assume all users are evil, and you should be fine.
A connection doesn't have to come from a browser - anyone can write their own HTTP requests with a telnet client. There are probably specialized tools for this as well, and they wouldn't be hard to create.
I'm new to PHP and I'm following a tutorial here:
Link
It's pretty scary that a user can write php code in an input and basically screw your site, right?
Well, now I'm a bit paranoid and I'd rather learn security best practices right off the bat than try to cram them in once I have some habits in me.
Since I'm brand new to PHP (literally picked it up two days ago), I can learn pretty much anything easily without getting confused.
What other way can I prevent shenanigans on my site? :D
There are several things to keep in mind when developing a PHP application, strip_tags() only helps with one of those. Actually strip_tags(), while effective, might even do more than needed: converting possibly dangerous characters with htmlspecialchars() should even be preferrable, depending on the situation.
Generally it all comes down to two simple rules: filter all input, escape all output. Now you need to understand what exactly constitutes input and output.
Output is easy, everything your application sends to the browser is output, so use htmlspecialchars() or any other escaping function every time you output data you didn't write yourself.
Input is any data not hardcoded in your PHP code: things coming from a form via POST, from a query string via GET, from cookies, all those must be filtered in the most appropriate way depending on your needs. Even data coming from a database should be considered potentially dangerous; especially on shared server you never know if the database was compromised elsewhere in a way that could affect your app too.
There are different ways to filter data: white lists to allow only selected values, validation based on expcted input format and so on. One thing I never suggest is try fixing the data you get from users: have them play by your rules, if you don't get what you expect, reject the request instead of trying to clean it up.
Special attention, if you deal with a database, must be paid to SQL injections: that kind of attack relies on you not properly constructing query strings you send to the database, so that the attacker can forge them trying to execute malicious instruction. You should always use an escaping function such as mysql_real_escape_string() or, better, use prepared statements with the mysqli extension or using PDO.
There's more to say on this topic, but these points should get you started.
HTH
EDIT: to clarify, by "filtering input" I mean decide what's good and what's bad, not modify input data in any way. As I said I'd never modify user data unless it's output to the browser.
strip_tags is not the best thing to use really, it doesn't protect in all cases.
HTML Purify:
http://htmlpurifier.org/
Is a real good option for processing incoming data, however it itself still will not cater for all use cases - but it's definitely a good starting point.
I have to say that the tutorial you mentioned is a little misleading about security:
It is important to note that you never want to directly work with the $_GET & $_POST values. Always send their value to a local variable, & work with it there. There are several security implications involved with the values when you directly access (or
output) $_GET & $_POST.
This is nonsense. Copying a value to a local variable is no more safe than using the $_GET or $_POST variables directly.
In fact, there's nothing inherently unsafe about any data. What matters is what you do with it. There are perfectly legitimate reasons why you might have a $_POST variable that contains ; rm -rf /. This is fine for outputting on an HTML page or storing in a database, for example.
The only time it's unsafe is when you're using a command like system or exec. And that's the time you need to worry about what variables you're using. In this case, you'd probably want to use something like a whitelist, or at least run your values through escapeshellarg.
Similarly with sending queries to databases, sending HTML to browsers, and so on. Escape the data right before you send it somewhere else, using the appropriate escaping method for the destination.
strip_tags removes every piece of html. more sophisticated solutions are based on whitelisting (i.e. allowing specific html tags). a good whitelisting library is htmlpurifyer http://htmlpurifier.org/
and of course on the database side of things use functions like mysql_real_escape_string or pg_escape_string
Well, probably I'm wrong, but... In all literature, I've read, people say It's much better to use htmlspellchars.
Also, rather necessary to cast input data. (for int for example, if you are sure it's user id).
Well, beforehand, when you'll start using database - use mysql_real_escape_string instead of mysql_escape_string to prevent SQL injections (in some old books it's written mysql_escape_string still).
Recently, I had an audit run on some of my sites by a client. One of the things they came back with was that I could be sanitizing the input data a little better as people could still cause potential harm to the database.
The function below is what I am currently using (a leftover from the old developer) but I cannot see where the potential issue may lie.
The string that gets passed through to the database will be displayed via XML which in turn is read by a Flash application.
Could anyone tell me what I might be missing? Thanks
function secure_string($string)
{
return (strip_tags(addslashes(mysql_real_escape_string(
stripslashes($string)))));
}
Better use the new PHP function filter_var() for cleaning input. New and better.
It looks like there's too much going on in that function. mysql_real_escape_string() already escapes everything you need to escape, so there's no need to run addslashes() on that. In fact, it could do more harm than good by escaping the backslashes mysql_real_escape_string() creates.
mysql_real_escape_string is the last step, you shouldn't use it your application logic. It's sole purpose is to pass strings to the database, so use it only when constructing queries. You can pass anything to mysql_real_escape_string and it will make sure you can safely store it in the database.
For the rest, it depends what do you want. If you want to strip tags, use strip_tags, etc.
Depends on where the "secured" string will be used. If it's going to be used in the database, you only need mysql_real_escape_string(), nothing more. If it's going to be displayed in html, you only need htmlentities(), nothing more. In short: your code is doing way too much, which could even be harmful.
If you want to store it in the database for displaying it in html lateron (like a comment, for example), you should be using mysql_real_escape_string() when storing the string and htmlentities() when displaying it.
If your server uses php 5.2 or better, you should use filter_var for the XML part.
$output = filter_var($input, FILTER_SANITIZE_STRING);
To store something into your database, use PDO and parameterized queries.
It's a misnomer to try and fix the problem at input time, since the problem happens at output time. See my answer over here:
What’s the best method for sanitizing user input with PHP?