Feature
Post

Category
Code


Basic PHP Hacks and How to Secure your App

Lately, I have been working on a project with a group of developers that I found. In this modern day, security is a must. You have people stealing your server information, database login details, user passwords, script insertions and more. Several years ago security wasn’t a big deal, but now is a desperate time, and desperate times call for desperate measures. Because of this, I have refined my knowledge of hacks that others can do and how to prevent them (or at least prevent most).

Password Encryption

These days, it is necessary to encrypt your password in a database. Sure you may encrypt your password with md5 or sha1, but there are methods to decrypt these methods. A good concept is to “do multiple times,” and, in this case, do encryption multiple times. No, I do not mean to md5 twice. There are methods such as salting, or adding extra input to the encryption. One great method is to generate a secure hash on user registration or using information you may already know that the outside world may not, such as registration date (timestamp is best).

// User registration
$pass = md5($pass . sha1($pass . time()));
?>

In the above example, we used two functions for encryption: md5 and sha1. It is usually best to use many methods of encryption such as above. Extremely few people know how to decrypt md5, and the same for sha1. Together, it is virtually impossible to figure it out. One other thing I want to point out is that we salt twice in that php above. First in sha1 using time(), and the second using the sha1 encryption in md5. The more secure the better, although do not overdo it. Please.

Limit Authorization in the Database

When installing an application, many users just use the default user settings for their database details. The user will have access to every permission in the DB that he can get (depending if on hosted domain or self managed server). If a hacker comes in and gets your details, you are really screwed because they have permission to delete any database, tables, rows, and columns, insert meaningless data, update current data with wrong details or worse. If you follow the password encryption method above, it would be difficult for one to decrypt your passwords.

The best thing to do if you are the installer is to verify which permissions you need in order to run the software correctly. On the other hand, if you are a programmer, please know which permissions, precisely, you need to have the program run without the disruption of errors.

Cross Site Request Forgeries

These are trickier than the above, though are really simple to prevent. In order to know if your software or web site can be hacked with CSRF’s, ask your self if it accepts $_GET or $_POST (or $_REQUEST). To portray how malicious this can be and no matter how secure your application is, Gmail was found to be vulnerable by CSRF’s. There was a method for seeing all the contacts a person had and more. If you have an online store, for example, someone can easily forge a request that the user has, like the following:

buy_product($_REQUEST['product_id']);
?>

The above has several things wrong with it. First, it does not even check if the key ‘product_id’ exists. The second is that is uses $_REQUEST which is bad. This predefined, global variable built into the PHP core contains the values of $_GET, $_POST, and others depending on your php.ini configuration file. To forge a request one can easily alter the HTML using Firebug or any other tool in the browser, or outside the browser and save it, like so:

<img src="buy.php?product_id=5" alt="Welcome!" />

This is the best reason to just switch to $_POST when possible. By definition made by the W3C, POST should be used when an action is made and GET should be used to retrieve information. HINT: When you buy something, you are performing an action. You may also be thinking that CSRF’s also work with POST. Well, it does. What I do, and same for countless other applications, is to use form tokens. These basically hold some information of the user that requested the form. You may either simply md5 the user’s IP Address or a combination of other things, or store some information in the database with a unique token id. This token will be placed in the form as follows:

<input name="token" type="hidden" value="&lt;?php echo generate_token(); ?&gt;" />

and

function generate_token()
{
return md5($_SERVER['REMOTE_ADDR']);
}

if (isset($_POST['submit']) &amp;&amp; !empty($_POST['product_id']))
{
if ($_POST['token'] == generate_token())
{
buy_product(intval($_POST['product_id']));
}
}
?&gt;

There are many great improvements here, especially one that we will discuss later on the article. IP Addresses can not be forged, atleast I do not think so. The truly best form of doing tokens is generating a completely new token based on time and other factors and inserting that into the database. When the form is submitted, you select the token from the DB and verify it from there. We now use $_POST and check if the form has been submitted and if the product id is not empty. If the form has been submitted, we then check if the two tokens are the same. A solution we will discuss later on is to cast the product_id into an integer since that is what it is supposed to be. This prevent SQL Injections.

Cross Site Scripting

I guess the acronym CSS was well known by programmers and designers so they changed it to XSS. This is similar to CSRF’s, though quite the opposite and mostly involves forms and the output of the application. Basically think of filling out a form and inserting some html and javascript such as the following:

<script type="javascript"><!--
alert('This page is not XSS-proof');
// --></script>

Imagine inputting that for a field that can be seen publicly like your username, full name, email address, and more. If you do not do the proper measures for preventing scripting, any user that visits a page containing the field in which the above is submitted will be notified that “This page is not XSS-proof.” The are several ways to prevent this script from being called. You can either remove the html tags or use html entities. I prefer using html entities:

//Check if the form has been submitted and that
// $_POST['username'] is not empty before continuing
$username = trim(htmlentities($_POST['username']));
?&gt;

This can lower your chances of being hacked using XSS significantly by converting <script> to &lt;script&gt; and other things like & to &amp;

SQL Injection

This last one is one of the most difficult since you have to do the same for every input you get that will go into the database. There really is not one justified method for preventing this. There are several and they do not protect against everything either, only most. If you ask someone for their usernameand password to login, a hacker who specializes in this may input something that can essentially modify your query.

$sql = 'SELECT username, password FROM users WHERE username=' . $_POST['username'];
?&gt;

With the above query, someone can easily input the following without quotes ‘foobar OR 1=1′. This query every user in the database with their username and password, because of the fact that 1 = 1 without even caring about the username being foobar. To prevent this you can do several meaningful things. Distinguish tables and columns from values using `, distinguish string values using “, escape using the database’s own escape function, and cast each value to what it is supposed to be. Here is the final product:

$sql = 'SELECT `username`, `password` FROM `users` WHERE `username`="' . mysql_real_escape_string($_POST['username']) . '"';
?&gt;

Although I did not present type casting in the above example (except in the last CSRF example), you can easily either do intval to whatever needs to be an integer and floatval for decimal numbers. Remember the tips explained above for SQL injections do not cover everything, and would be virtually impossible to prevent everything.

As a last note, I have explained how to prevent password hijacking, database corruption, CSRF and XSS attacks, and SQL injections. I hope you think of this tutorial every second you allow user input. With this guide, you will save yourself and any potential users headaches.

Good luck!


  1. By Wally Lawless posted on September 11, 2008 at 10:25 am
    Want an avatar? Get a gravatar! • You can link to this comment

    You beat me to it, I was just about to post an article somewhat similar to this one on my own site.

    One thing that I did want to point out, it is possible to fake an IP address, however it is not a trivial thing to achieve. Just thought I would point out that it is in fact possible.

  2. By Nils posted on September 11, 2008 at 12:18 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    *Decrypt* MD5 and SHA1?? Are you serious?? Do you even know what a hash function is?? Once something is hashed, there is no way of getting back the original information, unless you go at it with brute force comparison.

    Just 1 time MD5 or 1 time SHA1 is MORE than enough for any common web application. Stop the useless FUD.

  3. By Jim posted on September 11, 2008 at 3:17 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    Nils, you’re an idiot – Rainbow tables are everywhere and many are freely distributed. I’ve already used them to quickly (because they are all databases after all so its easy to say “SELECT plain_text FROM rainbow_table WHERE hash='{$StolenPasswordHash}'”) do a reverse look up on a hashed password. Salting is respected as a GOOD practice for a reason. Quit making useless comments, unless your point was to alert us to your ignorance.

  4. By Peter posted on September 11, 2008 at 3:20 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    “unless you go at it with brute force comparison”

    I have done a little research into that recently, and it appears to be simpler than it seems at first – especially if whatever is encrypted is a dictionary word. It gets significantly more difficult if you are salting the values, but what I found certainly opened my eyes – MD5 alone really isnt that secure.

  5. By LeVache posted on September 12, 2008 at 9:50 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    Using SHA1 and MD5 like that doesn’t increase security any more than adding a random salt, but it probably uses more processing time. There are published attacks on MD5, but even if someone gets their hands on your salted hashes (mmm–good with bacon fat) I doubt they’d be able to get passwords out it, even if they found a collision (and you’d have bigger problems to worry about–like how they got that information).

  6. By Ryan posted on September 12, 2008 at 9:53 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    MD5 and SHA1 are not encryption, they are hashing. There is a difference. Read on it.

  7. By Chris posted on September 12, 2008 at 11:45 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    I would not follow this crypto advice at all! Rehashing a hash makes it, by degrees, LESS SECURE than one hash, as you’re increasing the number of possible collisions.

    Try learning something about what you are feeding others BEFORE you post it.

  8. By theY4Kman posted on September 13, 2008 at 1:11 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Yes, Jim, salting is a good practice. However, Nils is right. If you think salting a hash with another hash is a good way to go about it, you don’t know enough about what you’re talking about.

    Secondly, man, don’t insult him as if he dissed you personally. He made a valid point.

  9. By miram posted on September 13, 2008 at 6:47 am
    Want an avatar? Get a gravatar! • You can link to this comment

    First of all: I’m not a PHP programmer so I might have missed something.

    But salting with time() makes no sense. First of all the salt is not appended to the hash in your example, so you would never be able to verify the password. If you planned to store the timestamp separatly you would have to make a second call to time(). You can not assume that the two calls would give the same result.

    Also doing a MD5 (128 bits, partially insecure) on a SHA1 (160 bits) would not improve security, it would decrease it.

    I have posted a modified version below.

    // store hash
    $salt = time();
    $pass = $salt.’-‘.sha1($pass . $salt));

    / / check password
    $hash = explode(‘-‘, $stored_hash);
    echo (“This is salt part:”.$hash[0].” This is SHA1 part:”.$hash[1]);
    if ($hash[0].’-‘.sha1($pass . $hash[0])) == $hash[1])
    {
    echo “Correct password”;
    }

  10. By miram posted on September 13, 2008 at 6:54 am
    Want an avatar? Get a gravatar! • You can link to this comment

    OK, did a cut-paste-modify-withoutthinking. The if-statement in the comment above should be:

    if (sha1($pass . $hash[0])) == $hash[1])

  11. By nateb posted on September 15, 2008 at 9:45 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Websites like http://wordencrypter.com and http://worddecrypter.com do just that with creating a md5 /sha1 lookup table.

  12. By Montoya posted on September 17, 2008 at 9:29 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Just FYI, you can use the new mysqli extension for PHP instead of the old mysql, which has object-oriented query methods that use escaping. You can bind parameters and force them to be integers or strings, and when you bind strings, they are forced to be contained, so even a wayward quotation mark won’t be able to expose or modify the query. I’ve been using mysqli instead of mysql for almost a year now and I’m never going back!

  13. By mixusr posted on September 22, 2008 at 1:52 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Wasn’t there a post about hiding conf’s or something of that nature last year? I actually tried searching for it and couldn’t find it.

  14. By mixusr posted on September 22, 2008 at 1:53 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Nevermind, found it:
    http://www.devlounge.net/code/protect-your-wordpress-wp-config-so-you-dont-get-hacked

    Thanks for the great security posts!

  15. By Kevin Martin posted on September 22, 2008 at 9:02 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    For those of you who have comments about how my methods above are in accurate or less secure, this tutorial is only for *basic* means of accomplishing a safer application.

    @Miram: Thanks for your code and to clarify on mine above, I meant to put time() in a variable, though since this was only for learning what should be done, I assumed you guys would do the same. And yes, you can never assume two calls to time() will be the same.

    @those who think MD5 and SHA1 cannot be decrypted: If you search for methods on how to do this, you will find documents on how you can find collisions and certain patterns in the hash. And sure, there are rainbow tables out there, but they are only useful for plain MD5 and SHA1 but not for anything such as above, especially with a unique salt.

    Thanks everyone else for your comments and hope you give us authors more feedback. :)

  16. By adie posted on September 23, 2008 at 10:18 am
    Want an avatar? Get a gravatar! • You can link to this comment

    whoaa,. i’m learn a lot, i loved this blog and i’ll bookmark it for further resources, thanks for sharing :)

  17. By Custom PHP posted on November 12, 2008 at 7:05 pm
    Want an avatar? Get a gravatar! • You can link to this comment

    Using htmlentities is a major step in the right direction. Evaluating the input to verify it is what you expected it to be is always a good idea. You can limit what is acceptable as well.

  18. By Ryan posted on April 1, 2009 at 3:07 am
    Want an avatar? Get a gravatar! • You can link to this comment

    On the contrary, there are ways to decrypt BOTH md5 and sha1

    however they require time and a large processor to do the job

    that is why they are no longer among the ranking of true encryption algorithms,

    i believe at the time i post this, there are no TRUE encryption algorithms (which would be an algorithm that has passed the time test (5 years) without being cracked) and so for now they are using a temporary substitute, though not true, and it is:

    TRIPLE DES

    because Des was broken

    it is basically, using a Des algorithm three times

    this is currently the *best* encryption

    *according to the experts

  19. By Shubhamoy posted on May 6, 2009 at 7:01 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Hi Admin,

    Really shocking piece of information since I knew that PHP was foolproof but now I need to give it a thought. Is PHP on its own(without database) vulnerable?

    BR,
    Shubhamoy

  20. By Php Developer posted on October 2, 2009 at 8:33 am
    Want an avatar? Get a gravatar! • You can link to this comment

    Thank you for providing nice information on php security issue. I very thank full to you. Keep posting and inform us.

  21. TrackbackBasic PHP Hacks and How to Secure your App | Devlounge at ThnkMax Design - standards based web design, development and training » Some links for light reading (17/9/08)Top 50 Sources of Inspiration: Month of September | Peakflow DesignBasic PHP Hacks and How to Secure your App | Apni Library