A guide to better Application Security.

Discussion in 'PHP' started by David Lawrence, Nov 16, 2013.

  1. #1
    Ever needed to make you application more secure but really aren't sure how? I am going to tell you simple ways, that most veterans already know into making your application more secure. Things I will cover in this is, password hashing, and using salts.

    First lets start off with password hashing and salts.

    Now most of you already know, that you can use sha1 or md5 to hash passwords. But to me these methods feel very old, and can be unencrypted if needed to be by a good hacker. I like to make things a little more secure than just a simple SHA1 or MD5, and mix it up.

    // Mutli Encrypt a Password.
    for($i = 0; $i < 10; $i++){
        $password = md5(sha1(strrev($password)));
    }
    PHP:
    This block of code shows an example of how you would use both SHA1 and MD5 to encrypt a password, though you loop through a for loop and reverse the string each time. This way the hacker trying to decrypt your data will have a much harder time in decrypting these password or any field with this kind of encryption.

    Now salts.

    Salts are nothing more than a random generated string appended (or prepended) to another string to be encrypted.

    Here is a function I have created to generate a random string:

    function generate_password($length = 10){
    
            // Start with a blank password.
            $password = '';
    
            // Define possible characters to use.
            // Generated Password will be generated off these characters.
            $possible = "2346789bcdfghjkmnpqrtvwxyzBCDFGHJKLMNPQRTVWXYZ!@#$%^&*()-+=_";
    
            // Set max length, cause we cant have password longer than possible string.
            $maxlength = strlen($possible);
    
            // If lenght is larger than max length, set to max length.
            if($length > $maxlength)
                $length = $maxlength;
    
            // Set up count to stop loop at desired length..
            $i = 0;
    
            while($i < $length){
    
                // Pick a random character.
                $char = substr($possible, mt_rand(0, $maxlength-1), 1);
    
                // Have we already used this character before?
                if(!strstr($password, $char)){
    
                    // If not, add to the end of password and increase count.
                    $password .= $char;
                    $i++;
    
                }
    
            }
    
            // Finally return password!
            return $password;
    
        }
    
    PHP:
    I am not going to go into how this function works, but generally you call the function and supply (or not) a INT any number. I have mine defaulted to 10 but you can change the default length.

    Now how do we use this?

    $salt = generate_password(6);
    // Mutli Encrypt a Password with a salt.
    for($i = 0; $i < 10; $i++){
        $password = md5(sha1(strrev($password . $salt)));
    }
    Code (markup):
    Notice here our password encryption is almost identical, except we added a salt (a random generated string) to the end of the salt. You can also prepend a salt by swapping $password and $salt. This will encrypt the password with a salt attached.

    Note that when storing passwords in a Database or any other type of long term storage (or even sessions) you need to store the salt seperate from the password. As each password has a unique salt you need to be able to retrieve that salt to check a decrypted string against a encrypted string as you need to append (or prepend) the salt. Alternatively you can use the same salt across all passwords, but I do not recommend that.

    Thats all you need. Now that you have these tools, you can utilize them by creating one big function to 'generate' a password, that would call generate salt (a rename of my generate password), and a function that would check a string against a encrypted password.

    This doesnt just work with passwords. It will work on everything you need to permanently encrypt. Please note that you can not decyrpt this data, so stay tuned for when I cover decryptable data and how you can use it to better your CSRF protection.

    Thanks guys
     
    David Lawrence, Nov 16, 2013 IP
  2. nico_swd

    nico_swd Prominent Member

    Messages:
    4,153
    Likes Received:
    344
    Best Answers:
    18
    Trophy Points:
    375
    #2
    First off, I'm glad you care about security.

    Second, none of your methods are good, from a cryptographical point of view. I'll give you some further explanations a little later, if no one else beats me to it.
     
    nico_swd, Nov 16, 2013 IP
  3. nico_swd

    nico_swd Prominent Member

    Messages:
    4,153
    Likes Received:
    344
    Best Answers:
    18
    Trophy Points:
    375
    #3
    Rehashing an existing hash is not good, because it actually becomes less secure.

    
    // Mutli Encrypt a Password.
    for($i = 0; $i < 10; $i++){
        $password = md5(sha1(strrev($password)));
    }
    PHP:
    At this point it doesn't matter if you add a salt or reverse the password string. If a user picked a good password containing characters from a-z, A-Z, 0-9, and other symbols, by rehashing it you pretty much take everything good about it out and throw it in the trash. It's because sha1() only returns characters from a-f and numbers from 0-9. No capital letters, and no special characters. So the second you rehash the sha1 hash with md5, you strip out all the good stuff.

    There's even a note on both manual pages (md5 and sha1) on php.net:
    If a hacker manages to obtain the salted hash, he pretty much has no other choice than to brute-force it. This could either result in the original password, or a collision. So it doesn't matter how many times you rehash the password. It's not going to take more or less time. It's more secure if you don't rehash it at all, and especially if you don't use multiple hashing algorithms.

    Your generate_password() function is not secure either. Mainly because mt_rand() is not good for cryptography as it's vulnerable to seed poisoning, and not as random as one would hope. So attackers may be able to predict the output.

    At first sight, avoiding repeated characters in the generated password seems like a good idea. But it actually makes brute-forcing a lot easier and faster. See, with repeating characters, you don't know what comes next. You have to check every single possible combination. But with non-repeating ones, you know what doesn't appear again in the whole password. Meaning, there are far less possible combinations. I'm pretty sure this was one of the main reasons Alan Turing was able to decipher messages from the Enigma Machine during WWII.

    So after telling you a lot of stuff you're not supposed to do, here's how you should do it:
    
    // http://php.net/manual/en/function.password-hash.php
    $hash = password_hash($_POST['password'], PASSWORD_DEFAULT);
    
    PHP:
    Yep, it's that easy. This will generate a secure hash using bcrypt (as of PHP 5.5), and a secure salt that's based on /dev/urandom. This is an excellent source of high quality randomness (based on noise from hardware drivers, etc), and it's recommended for cryptography.

    The password_hash() API comes with PHP 5.5. If you're using an older version, use this: https://github.com/ircmaxell/password_compat

    If you want to know more about the API, I wrote a detailed post on my blog:
    http://blog.nic0.me/post/63180966453/php-5-5-0s-password-hash-api-a-deeper-look-under

    EDIT:

    A couple of further notes regarding your post:
    It's actually recommended to append the salt, as it makes brute forcing harder. Don't know the exact technical details, but it has something to do with being able to "cache" the already calculated hash of the salt and being able to skip that step further on.

    Well yes and no. If you're storing the hash in the same database as the salt, it's likely that the attacker can get both anyway if your site is vulnerable to SQL injections etc. In the password_hash() API that PHP 5.5 provides, the salt is actually part of the returned hash which you would store in the same database field. Unless you store the salt somewhere else entirely, there's not much point in storing it separately.
     
    Last edited: Nov 16, 2013
    nico_swd, Nov 16, 2013 IP
    sarahk and ryan_uk like this.
  4. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,623
    Likes Received:
    725
    Best Answers:
    152
    Trophy Points:
    470
    #4
    However, not many providers use PHP 5.5, not will they on the bear future. Most hosts are on max 5.3 - heck, even the university where I do some work is running older versions.
     
    PoPSiCLe, Nov 16, 2013 IP
  5. nico_swd

    nico_swd Prominent Member

    Messages:
    4,153
    Likes Received:
    344
    Best Answers:
    18
    Trophy Points:
    375
    #5
    There's a library from IRCMaxell (ex PHP core developer) for PHP 5.3:
    https://github.com/ircmaxell/password_compat

    It works pretty much the same way.

    Um what? No.
    Yes, but it's not always an option.

    No.
    What?
    You mean a rainbow table lookup? Not possible if the hash is salted.
    GnuPG is encryption, and not a hashing algorithm. You don't want encryption for passwords, because it can be *drumroll* decrypted. You may use encryption for credit card numbers or something, but not for passwords.

    What? Ummm WHAT?

    Are we still talking about the same thing here?
    Now you've lost me.

    You sure do.
     
    nico_swd, Nov 17, 2013 IP
    ryan_uk likes this.