mysql_real_escape_string and html_special_chars enough? - php

This question gets asked a lot, but I still haven't found a straight answer on stackoverflow. Are these two functions sufficient or not? There are a lot of contradictory comments around the internet "yes its fine?, "no, never use it". Others say, use PDO, which I don't understand. I'm just a beginner to PHP, so I don't fully understand all of the ins and outs of security.
I've tried reading and understanding the following, but many don't make much sense to me.
http://ha.ckers.org/xss.html
Do htmlspecialchars and mysql_real_escape_string keep my PHP code safe from injection?
What if I use preg_replace to strip unwanted characters?
I'm incredibly confused and don't know where to start.
EDIT: Could someone please also recommend how I go about understanding prepared statements (assuming this is the best option).

Sam, if you are storing the input in a database, to avoid SQL injection and XSS then those two functions are enough. If you are storing passwords, you must encrypt the passwords with one-way encryption (that is they can not be decrypted).
Let me expand my answer:
First of all, SQL Injection is a method where a malicious user will attempt to modify your SQL statement to make it do their will. For example, let's say you have a login form. By inserting one of the following values into an un-protected form, I will be able to log into the first account without knowing the username or password:
' or 1=1 --
There are many versions of the above injection. Let's examine what it does to the SQL executed on the database:
The PHP:
mysql_query("SELECT * FROM users WHERE username='" . $username."' AND password='" . $password . "';");
When the above is executed, the following SQL is sent to the database:
SELECT * FROM users WHERE username='' or 1=1-- ' AND password='' or 1=1--';
The effective part of this SQL is this:
SELECT * FROM users WHERE username='' or 1=1
as the double dash (with the space afterwards) is a comment, removing the rest of the statement.
Now that gives the malicious user access. With use of an escaping function such as mysql_real_escape_string, you can escape the content so the following is sent to the database:
SELECT * FROM users WHERE username='\' or 1=1-- ' AND password='\' or 1=1--';
That now escapes the quotes, making the intended strings, just that - strings.
Now let's view some XSS.
Another malicious user would like to change the layout of a page. A well known XSS attack was the Facespace attack on Facebook back in 2005. This involves inserting raw HTML into forms. The database will save the raw HTML and then it will be displayed to users. A malicious user could insert some javascript with use of the script tag, which could do anything javascript can do!
This is escaped by converting < and > to &ltl; and > respectively. You use the html_special_chars function for this.
This should be enough to secure normal content on a site. However passwords are a different story.
For passwords, you must also encrypt the password. It is advisable to use PHP's crypt function for this.
However, once the password is encrypted and saved in the database as an encypted password, how can you decrypt it to check that it is correct? Easy answer - you don't decrypt it. HINT: A password always encrypts to the same value.
Were you thinking 'We can encrypt the password when the user logs in and check it against the one in the database', you are correct...

Depends on what you're using the input for and where it's going.
the only thing to assume when sanitizing input for php applications is that there are a lot of smart people out there, and if they really wanted to break your app, they'll find a way to do it.
following the guides you listed and other guides are a great start. but then every bit of sanitizing you do will incur a non-trivial amount of time and memory to perform.
so it all really depends on what you're using the input for and where it's going.
mysql_real_escape_string and html_special_chars are nice utilities, but you shouldn't depend solely on them and they're are also not always needed.
don't get discouraged as even mature and well programmed sites have to keep up with the latest in xss and other attacks. fun game of cat and mouse.
one place you can start is with a framework, i like codeignitor. go through their security classes to see how they deal with it.
or better yet, write a simple form processor and try to come up with ways to run arbitrary code yourself. you'd be surprised with how many ways you can come up with.

Assuming you are asking about security, there is one problem with mysql_real_escape_string.
This function has absolutely nothing to do with security.
Whenever you need escaping, you need it despite of "security", but just because it is required by SQL syntax. And where you don't need it, escaping won't help you even a bit.
The usage of this function is simple: when you have to use a quoted string in the query, you have to escape it's contents. Not because of some imaginary "malicious users", but merely to escape these quotes that were used to delimit a string. This is extremely simple rule, yet extremely mistaken by PHP folks.
This is just syntax related function, not security related.
Depending on this function in security matters, believing that it will "secure your database against malicious users" WILL lead you to injection.
A conclusion that you can make yourself:
No, this function is not enough.
Prepared statements is not a silver bullet too. It covers your back for only half of possible cases.

Related

Sanitizing PHP Variables, am I overusing it?

I've been working with PHP for some time and I began asking myself if I'm developing good habits.
One of these is what I belive consists of overusing PHP sanitizing methods, for example, one user registers through a form, and I get the following post variables:
$_POST['name'], $_POST['email'] and $_POST['captcha']. Now, what I usually do is obviously sanitize the data I am going to place into MySQL, but when comparing the captcha, I also sanitize it.
Therefore I belive I misunderstood PHP sanitizing, I'm curious, are there any other cases when you need to sanitize data except when using it to place something in MySQL (note I know sanitizing is also needed to prevent XSS attacks). And moreover, is my habit to sanitize almost every variable coming from user-input, a bad one ?
Whenever you store your data someplace, and if that data will be read/available to (unsuspecting) users, then you have to sanitize it. So something that could possibly change the user experience (not necessarily only the database) should be taken care of. Generally, all user input is considered unsafe, but you'll see in the next paragraph that some things might still be ignored, although I don't recommend it whatsoever.
Stuff that happens on the client only is sanitized just for a better UX (user experience, think about JS validation of the form - from the security standpoint it's useless because it's easily avoidable, but it helps non-malicious users to have a better interaction with the website) but basically, it can't do any harm because that data (good or bad) is lost as soon as the session is closed. You can always destroy a webpage for yourself (on your machine), but the problem is when someone can do it for others.
To answer your question more directly - never worry about overdoing it. It's always better to be safe than sorry, and the cost is usually not more than a couple of milliseconds.
The term you need to search for is FIEO. Filter Input, Escape Output.
You can easily confound yourself if you do not understand this basic principle.
Imagine PHP is the man in the middle, it receives with the left hand and doles out with the right.
A user uses your form and fills in a date form, so it should only accept digits and maybe, dashes. e.g. nnnnn-nn-nn. if you get something which does not match that, then reject it.
That is an example of filtering.
Next PHP, does something with it, lets say storing it in a Mysql database.
What Mysql needs is to be protected from SQL injection, so you use PDO, or Mysqli's prepared statements to make sure that EVEN IF your filter failed you cannot permit an attack on your database. This is an example of Escaping, in this case escaping for SQL storage.
Later, PHP gets the data from your db and displays it onto a HTML page. So you need to Escape the data for the next medium, HTML (this is where you can permit XSS attacks).
In your head you have to divide each of the PHP 'protective' functions into one or other of these two families, Filtering or Escaping.
Freetext fields are of course more complex than filtering for a date, but never mind, stick to the principles and you will be OK.
Hoping this helps http://phpsec.org/projects/guide/

PHP user input data security

I am trying to figure out which functions are best to use in different cases when inputting data, as well as outputting data.
When I allow a user to input data into MySQL what is the best way to secure the data to prevent SQL injections and or any other type of injections or hacks someone could attempt?
When I output the data as regular html from the database what is the best way to do this so scripts and such cannot be run?
At the moment I basically only use
mysql_real_escape_string();
before inputting the data to the database, this seems to work fine, but I would like to know if this is all I need to do, or if some other method is better.
And at the moment I use
stripslashes(nl2br(htmlentities()))
(most of the time anyways) for outputting data. I find these work fine for what I usually use them for, however I have run into a problem with htmlentities, I want to be able to have some html tags output respectively, for example:
<ul></ul><li></li><bold></bold>
etc, but I can't.
any help would be great, thanks.
I agree with mikikg that you need to understand SQL injection and XSS vulnerabilities before you can try to secure applications against these types of problems.
However, I disagree with his assertions to use regular expressions to validate user input as a SQL injection preventer. Yes, do validate user input insofar as you can. But don't rely on this to prevent injections, because hackers break these kinds of filters quite often. Also, don't be too strict with your filters -- plenty of websites won't let me log in because there's an apostrophe in my name, and let me tell you, it's a pain in the a** when this happens.
There are two kinds of security problems you mention in your question. The first is a SQL injection. This vulnerability is a "solved problem." That is, if you use parameterized queries, and never pass user supplied data in as anything but a parameter, the database is going to do the "right thing" for you, no matter what happens. For many databases, if you use parameterized queries, there's no chance of injection because the data isn't actually sent embedded in the SQL -- the data is passed unescaped in a length prefixed or similar blob along the wire. This is considerably more performant than database escape functions, and can be safer. (Note: if you use stored procedures that generate dynamic SQL on the database, they might also have injection problems!)
The second problem you mention is the cross site scripting problem. If you want to allow the user to supply HTML without entity escaping it first, this problem is an open research question. Suffice to say that if you allow the user to pass some kinds of HTML, it's entirely likely that your system will suffer an XSS problem at some point to a determined attacker. Now, the state of the art for this problem is to "filter" the data on the server, using libraries like HTMLPurifier. Attackers can and do break these filters on a regular basis; but as of yet nobody has found a better way of protecting the application from these kinds of things. You may be better off only allowing a specific whitelist of HTML tags, and entity encoding anything else.
This is one of the most problematic task today :)
You need to know how SQL injection and other attackers methods works. There are very detailed explanation of each method in https://www.owasp.org/index.php/Main_Page and also whole security framework for PHP.
Using specific security libraries from some framework are also good choice like in CodeIgniter or Zend.
Next, use REGEXP as much as you can and stick pattern rules to specific input format.
Use prepared statements or active records class of your framework.
Always cast your input with (int)$_GET['myvar'] if you really need numeric values.
There are so many other rules and methods to secure your application, but one golden rule is "never trust user's input".
In your php configuration, magic_quotes_gpc should be off. So you won't need stripslashes.
For SQL, take a look at PDO's prepared statements.
And for your custom tags, as there are only three of them, you can do a preg_replace call after the call of htmlentities to convert those back before your insert them into the database.

What is the proper way to secure data going in & out of my database?

I'm trying to secure my script a bit after some suggestions in the last question I asked.
Do I need to secure things like $row['page_name'] with the mysql_real_escape_string function? example:
$pagename = mysql_real_escape_string($row['page_name']);
I'm asking mainly because when I do secure every row I get some errors like when trying number_format() it throws number_format() expects parameter 1 to be double, string given while when it is not secured with mysql_real_escape_string it works.
Can someone clear this for me? Do I only need to secure COOKIE's or the row fetches too?
I got the suggestion in this post: HERE (look at the selected answer)
You're doing it backwards. Presumably $row is a row coming out of the database. You don't mysql_real_escape_string on the way out of the database, you use it on data going into the database to prevent SQL injection. It prevents people from submitting data that contains executable SQL code.
Once the data is safely in the database, you're done with mysql_real_escape_string (until you attempt to update that data). User data coming out of the database needs to be run through htmlspecialchars before it hits the page to prevent script injection.
Basically, on the way to the database, just before your insert/update runs, you need to escape potentially executable SQL. On the way to the browser, just before strings leave your app for the browser, you need to escape potentially executable JavaScript and/or interpretable HTML. Escaping should be the last thing you do with a piece of data before it leaves your app for either the browser or database.
This is by no means a complete answer.
Before writing any more code you need to stop and consider exactly what it is you are trying to accomplish.
In other words, what are you gaining by running the mysql_real_escape_string function?
Generally speaking, you escape data submitted by the client. This is to help prevent sql injection. Also, you should go further to actually validate that what the client sent in is acceptable (ie. "Sanity Check"). For example, if you are expecting a numeric entry, don't accept strings and range check the values. If you are expecting string data like a name, don't accept HTML, but again range check to verify length is acceptable. Both of these situations occur when the client submits data, not when you are writing it back out.
Going a little further, your cookies should be encrypted and marked with the httponly flag to tell the browser that it is not for use in client side script. Even with that, you shouldn't trust the data in the cookie at all; so go ahead and run your sanity checks and still escape those values in queries.
I highly recommend that you go to the OWASP website and read through all of the issues to get a better understanding of how attacks work and how to defend against them. Web App security is too important to just start coding without really knowing what's going on.
BTW, kudos to you for learning about this and trying to defend your site. Too many devs don't even think about security at all.
If you use the PDO extension to build clean requests, you can create functions that will do this (secure strings and define their type) :
An exemple where $text is a string of text and $number is an integer :
public function InsertThis($number, $text) {
$pdo = $this->getPdo();
$sth = $pdo->prepare("INSERT INTO my_table (number, text) VALUES (:number, :text");
$sth->bindParam('number',$number,PDO::PARAM_INT);
$sth->bindParam('text',$text);
$sth->execute();
}
http://php.net/manual/en/book.pdo.php
You only need to use mysql_real_escape_string() when inserting/updating a row where the values have come from untrusted sources.
This includes things like:
$_GET
$_POST
$_COOKIE
Anything that comes from the browser
Etc..
You should only use it when putting things into the database, not when you are taking things out, as they should already be safe.
A safer way altogether is to use the PDO class
mysql_real_escape_string does not "secure" anything. It escapes characters that can be used in sql injection attacks. Therefore the only values that you should escape are the ones supplied by your users. There should be no need to escape things that come out of your own database.

Login code sample which has been hacked via SQL Injection, although mysql_real_escape_string...

I use CodeIgniter, and having trouble with hacking. Is it possible to make SQL Injection to the login code below:
function process_login()
{
$username = mysql_real_escape_string($this->input->post('username'));
$password = mysql_real_escape_string(MD5($this->input->post('password')));
//Check user table
$query = $this->db->getwhere('users', array('username'=>$username, 'password'=>$password));
if ($query->num_rows() > 0)
{
// success login data
Am I using the mysql_real_escape_string wrong, or what?
No what have posted is not probably not vulnerable to sql injection. Although getwhere() could be doing a stripslashes(), I'm not sure.
Its likely that if there was SQL Injection that it is in another part of your application. The attacker could use this vulnerability to obtain your extremely weak md5() hash, crack it, and then login. Use any member of the sha2 family, sha-256 is a great choice.
If your site has been defaced then I seriously doubt that it is sql injection. Its difficult to automate the exploitation of sql injection to deface websites, but it is possible. I would make sure that all libraries and installed applications are fully updated. Especially if you have a CMS or forum. You could run an OpenVAS scan against your site to see if it finds any old software.
Database
Judging by your code I see you're not using the lastest CI version (2.0.2 as of 06/12).
As stated in the changelog the getwhere() function (which is now called get_where()) has been abandoned as for version 2.0. As for everty application out there you're strongly suggested to upgrade your current version, as there has been a lot of bugfixes in the meantime and you should always rely on the safest version available.
mysql_real_escape_string usually is considered 'enough' to give a good level of safety in your queries, but as it happend to its predecessor (mysql_escape_string) it isn't 100% safe against all kind of attack, so relying interely on that is not the best practice around. Although safe, there are still attacks that can go past this filter.
Check, among the many, this question on SO for further information about this.
In codeignier:
If you were developing your custom application, I'd suggest you to at least use the mysqli extensions or, better yet, the PDO class; prepared statements are undoubtely safest and should be favoured over everything else.
But we are in the framework context, and Codeigniter comes with 3 great ways of safely querying your database, applying the right tool to the right input without you having to worry about that. I'm talking about query bindings and manual escaping with $this->db->escape() family and the Active Record Class
You can find examples of use at the urls I just linked, or read the answers from other peers here, so I won't go into the details of each procedure in this post.
Password
Regarding your password, as already stated by other users, md5() is a now flawed hashing alghoritm. There are rainbow tables out there that can crack your md5 password in a relatively short amount of time, so you're better off with higher security level hashing algorhytms, like sha1() or sha256, sha512, and other
In codeigniter:
Codeigniter comes with a security helper class, which provides you with a handy function, do_hash() (might be dohash() in your older installation), which can be given the hashing alg. as paramter (currently I think it supports only md5 and sha1) and defaults to sha1() anyway.
Other observations
I'm not entirely clear on why you blame your login for your SQL injections. Are those the only 2 forms in your whole application? You dind't provide the info to tell if you use $_GET parameters or you follow the native URI segmentation, but I believe you're doing like this so I assume you're safe from this point of view. You should make sure then that there's no other input form in your website which contains input going into the database, otherwise you can secure your login how much you want, but someone could penetrate through a backdoor and read from there your database table and get log into your website in a "legitimate" way.
Moreover, there can be other source of intrusion, like a compromized cookie for example. As a piece of advice, whenever you choose to use a framework (and you're doing yourself a bigger favour than developing from scratch and all by yourself) you should tend to use MOST of its features, expecially when it comes down to security. It's a huge and very delicate question, so you MUST give this topic your top priority, and a well developed framework, with a huge community and frequent updates is the closest to safety you can get. Therefore, you're adviced to update your CI installation (guides can be found here in their manual. Choose your version and follow the instruction), always use the top tools you're given for each task, and don't think that barring your door will make you safe from an intrusion from your windows. Always check thoroughly and investigate all possibile causes.
Late Addendum: Don't forget XSS, CSRF, session fixations, and other hot security problems.
Have a look at this old SO question.
I would use:
$sql = "SELECT * FROM users WHERE username = '?' AND password= '?'";
$dbResult = $this->db->query($sql, array($this->input->post('username')),array($this->input->post('password')));
If this is an ongoing hack, then seriously consider putting some logging in place to record the username/password in a file somewhere. If it is an sql injection via this login snippet, then it would show up in this new log file somewhere. And while you're at it, if you can, log the generated SQL query as well.
In any case, remember that mysql_real_escape_string() only covers mysql's metacharacters: single-quote, double-quote, semi-colon, etc... It is still entirely possible to hack the login function via mangling of a boolean parameter. Impossible to say if your "getwhere" function is vulnerable, but consider the case where the submitted password is "xyz OR (1=1)". The generated query might end looking something like
SELECT id FROM users WHERE users=someusername AND password=xyz OR (1=1);
Perfectly valid query, and has also passed through mysql_real_escape_string intact because it didn't contain any of the critical metacharacters.
It's my understanding that Active Records will properly escape the values for you. So, with $query = $this->db->getwhere() you do NOT need to use mysql_real_escape_string().
But, you should always use some sort of Controller validation. In particular, you can limit the username and password to certain characters via a regular expression. This is always suggested.
Furthermore, if you are getting hit with typical attacks often then look into using PHPIDS. It's an Intrusion Detection System that will help you prevent the attacks from actually causing any damage, as you can thrown an error or notice of some sort.
You may also wish to view this tutorial on Security issues.
http://net.tutsplus.com/tutorials/php/codeigniter-from-scratch-security/
If all your functions and accessors do what their names seem to suggest, then this script is not vulnerable to SQL Injection.
As for whether you are using mysql_real_escape_string properly... yes and no. Yes, you have the right idea, but you're actually using it excessively. If you MD5 the password, then that input is now clean and can't be used for injection, so the mysql_real_escape_string call is superfluous. So this would be fine:
$username = mysql_real_escape_string($this->input->post('username'));
$password = MD5($this->input->post('password'));
As previously mentioned, MD5 is a rather weak hash algorithm by itself. Look into SHA and salting a hash.
To counter a previous answer posted, if a user inputs "xyz OR (1=1)" as a password, that poses absolutely no threat to his script for two reasons.
1) MD5 hashing would convert that to a harmless hash (e.g. 'd131dd02c5e6eec4693d9a0698aff95c'), making the query something along the lines of SELECT id FROM users WHERE users='username' AND password='d131dd02c5e6eec4693d9a0698aff95c';
2) mysql_real_escape_string prevents 'string' injections, that is injections that would require a single or double quote to break out of the expected SQL query, but does not prevent (for example) injection with numerical input, which you would need to use type-casting to block.
Everything is really contingent on your "$this->db->getwhere" call. If that just builds the query normally without doing anything funky (like stripping quotes, or treating strings as integers [which would throw an error anyway], etc) you are simply not vulnerable in this script. If you could give more details on the 'type' of hacking you've experienced, we could probably give you some guidance where to look. Were you defaced?
I haven't played with CodeIgnitor, but expect that their built in input/database functions wouldn't do anything weird like that - so unless you edited the getwhere function yourself, you're probably fine in this particular spot.
The code you provided is not vulnerable for SQL injection, but you should never put passwords into queries, not even hashed, because you cannot be sure if the query is saved to the server's log or not, and you usually don't have control about who can have access to that.
Query/load the user's record by username, and then compare the hash of the password in $_POST to that.
The code seems to resistant to injection attacks. You are taking md5 of password field which leaves out any injection attack on that field. The vulnerable field is username which you are escaping anyhow. Are you sure that this is an injection attack?
Is there any other field like access_token fields passed along to prevent CSRF
Following are probable causes of strange behavior:
You are using mysqli driver in database config and escaping string according to mysql lib. Try providing $this->db->conn_id as second parameter to mysql_real_escape_string and check your php logs for warnings.
mysql_real_escape_string needs connection link identifier as second parameter. If you don't provide it you will experience strange behavior. I have suffered this with codeigniter. In my case mysql_real_escape_string was returning empty strings. If the case is same with you, check that you don't have empty passwords and usernames in your users table (very less probable).

Is using a database-level MD5 function a bigger security risk than an application level function?

I've got a chunk of code that validates a user's username and password, which goes something like this:
$sql = "SELECT *
FROM user
WHERE
username='{$_POST['username']}' AND
password=MD5('{SALT}{$_POST['password']}')";
Is this any more/less secure than doing it like this?
$sql = "SELECT *
FROM user
WHERE
username='{$_POST['username']}' AND
password='".md5(SALT.$_POST['password'])."'";
Regardless of where/if escaping is done, is the first method vulnerable to sql injection attacks? Would the answer be the same for other database engines besides MySQL?
You should use prepared statements instead and have a look at this question.
Speaking about injection, both ways are secure, if you properly escape variables.
The first case will be more vulnerable, if you use complete query logging, and so the password will appear as plain text.
Besides, if your system is affected by some virus that works as proxy between your script and database, it'll be able to catch your password.
One last problem that you may encounter (quite rarely, in fact), is when the system is inflicted with a virus, that reads sensible data from memory.
I hope this makes sense.
Oh god, please tell me you're doing some type of mysql_escape_string or mysql_real_escape_string or AT LEAST addslashes or addcslashes to any $_POST variables before you insert them into a raw MySQL statement?
I think the most secure way to do this is to:
a) use filter_var or preg_replace to get rid of extraneous characters from the $_POST['username']
b) SELECT the row by the username from MySQL (also grabbing the digested password)
c) compare the message digested version of the password from the $_POST to that of the retrieved row (assuming you don't leave your password cleartext) in your application code, not in the SQL statement
If you do it this way, there's only 1 possible place for injection (username), and it's pretty impossible when you're doing a preg_replace( '/\W/', '', $_POST['username'] ) which removes anything not A-Za-z0-9_- (or change to your username whitelist of characters).
However, if you're doing rock-solid proper sanitization, it really doesn't matter where you do your comparison. Theoretically, though, I'd allow for the least possible interaction with user input and raw SQL statements (i.e. only SELECTing by username and comparing outside of your DB).
To start off with MD5 is prevent to be an insecure algorithm and should never be used for passwords. You should use a staled sha256 and most databases do not have this function call. But even if the database did I think its a bad idea. Not a very bad idea, but its best to keep as few copies of your password around. Often the database can be on a completely different machine, and if that machine where compromised then the attacker could obtain clear text passwords by looking at the quires. In terms of SQL Injection, there is no difference in security and judging by your queries you should be more worried about SQL injection.
Regardless of where/if escaping is done, is the first method vulnerable to sql injection attacks?
SQL injection will not occur if proper escaping and sanitizing takes place
Would the answer be the same for other database engines besides MySQL?
I think you should look more at the expense taken to perform one action over another. The first method would take less time to execute than the second method, ceteris paribus.

Categories