Jump to content
Byte-ul

[PHP Series] Password Security

Recommended Posts

Posted

Passwords are probably the most sensitive piece of user information you'll be storing within your web application. When people register on your website, they place a certain amount of trust in the security methods used within your web application, and expect their sensitive information to be securely protected.

Every now and then, you'll hear large websites become victims of attacks that have lead to their database being compromised. This causes not only user account problems for that website, but also for other websites where those same affected users have used their same password there. There's also a lost of trust from users and bad publicity to deal with amongst other things. For this reason, it is critically important for you to ensure your website is secure. But mistakes happen, and so you need to ensure that if in the event your database is compromised, your users' passwords are securely stored (i.e. hashed) within your database so that they can't be revealed easily.

I will therefore be talking about how to properly hash passwords to ensure security in case of a database breach. But first, let's review the hashing algorithms that you should not use within your web applications.

Definition List


  • Hashing
    • A one-way process of turning a string of characters into digest according to the hashing algorithm used.

Digest

  • The regurgitation of characters from a hashing process.

Collision
  • Where different string inputs have the same digest. This occurs because of the potentially infinite input of characters, with only a finite output (which is dependent upon the digest size of the hashing algorithm).

Collision Rate

  • The frequency of collisions in a hashing algorithm. The smaller the digest, the higher the collision rate (and vice-versa).

Salt
  • A randomly generated string of characters that is hashed along with a password to prevent dictionary and rainbow table attacks.

Hashing Algorithms You Should Not Use

There are a few cryptographic hashing algorithms you should eschew when building your web application. They are either considered 'broken' or don't have a sufficient amount of computations to be considered 'secure' anymore.

MD5

  • MD5 is a now-antiquated hashing algorithm that produces a 128-bit (32 character) digest of hexadecimal characters. Security flaws have been found in the algorithm, and because of its small digest, it is vulnerable to higher collision rates than other modern-day hashing algorithms. This algorithm is also very quick at generating a digest, and so whilst it's good to use for integrity checks (such as hashing files and comparing the digest), it is not suitable for usage upon passwords.

SHA1
  • SHA1, like MD5, is also considered outdated. It produces a 160-bit (40 character) digest of hexadecimal characters. Security flaws have too been found in this algorithm, and whilst its digest is larger than that of MD5 (giving it a slightly lower collision rate), it still has an overall high collision rate. This algorithm is also very quickly at computing digests, and because of these reasons it should also be eschewed if you're looking to protect sensitive information via hashing.

Hashing Algorithms You Could Use

The following is a list of hashing algorithms that you could use in your web applications. They're more preferred than the aforementioned (since they are not considered 'broken' and have a sizeable digest), but less preferred than the next sub section of algorithms (due to their speed).

SHA2 Family

  • The SHA2 family supersede SHA1 by creating longer digests (that are therefore more computationally expensive). These algorithms are slower to compute than the other two aforementioned hashing algorithms, and because their digests are much longer (SHA-256, SHA-384, and SHA-512 generate a 64, 96, and 128 character digest respectively), they have a much lower collision rate.
    Here's an example of using PHP's built-in
hash_hmac() function (please read the "enforce security with a salt" section for more information on 'salting' your password):

<?php

$digest = hash_hmac('sha512', 'MyPassword', 'salt_here');


Unfortunately, the SHA2 family are still very fast to compute (and speed is one area a hashing algorithm doesn't want). This problem will only ever get worse as computing power increases, and so these still aren't the preferred hashing algorithms to use.

Hashing Algorithms You Should Use

These algorithms are the preferred way to hash sensitive information. The reason being is that they have the ability to specify a work factor, where we can say how expensive we'd like our hashing to be. This is important because as computing power increases yearly (according to Moore's Law), we want to ensure that our hashing algorithm takes longer to compute (i.e. be scalable with hardware) - otherwise it will make generating rainbow tables a lot easier with time. This is something none of the aforementioned hashing algorithms allow for, which is why the following algorithms are the most preferred.

bcrypt

  • Bcrypt is something every security-conscious developer should look into. The API for using bcrypt prior to PHP 5.5 was something that confused many people new to the password hashing scene. Fortunately, this changed in PHP 5.5 with the advent of the
password_ functions (see this tutorial covering them too). Since then, a couple of libraries have been released that expose the same API as the new password_ functions to make using bcrypt easier (see here for more information about this).
There are also numerous other posts on both StackOverflow and Security.StackExchange that are well worth reading through for those of you who have an interest in cryptography and cryptanalysis (like myself):

Should I Nest Hashing Algorithms?

Nesting hashing algorithms is where the output of one hashing algorithm (the digest) acts as the input of another hashing algorithm, and so we end up rehashing generated digests multiple times. This alone is not good enough for security purposes, and will in fact make your passwords less secure. This is because the input going into a hashing algorithm has an infinite number of possibilities, whereas the digest coming out the hashing algorithm is finite depending upon the digest size and characters used. Thus, we increase the collision rate through lack of entropy when chaining simple hashes together.

Tip:

  • Cryptography is a complex and very involved topic. It can be very easy to fool yourself into thinking you have created a cryptographically strong hashing algorithm, when in fact you've only weakened the original hashing algorithm used. A good rule of thumb is to stick to the widely adopted hashing algorithms and to avoid creating your own unless you really know what you're doing.

Nesting hashing algorithms does, however, have the advantage of increasing the computational time, which makes brute force attacks longer (to the point where they may no longer be a cost-effective approach). When done right, nesting hashing algorithms can also mean the collision rates do not increase either. One good example is the PBKDF2 key derivation algorithm. This is where the password is injected into each round of the hashing chain, therefore keeping the entropy there to prevent increasing collision rates, but also making the finishing digest more computationally expensive to generate:

hash(hash(hash(hash(hash(hash(password+salt) + password+salt) + password+salt) + password+salt) + password+salt) + password+salt)


PHP has a function for this: hash_pbkdf2(). Unfortunately it is only available to those running PHP 5.5 or higher - but again, using bcrypt is still the preferred method of hashing passwords.
Enforce Security with a Salt
The main purpose of a salt is to prevent both precomputed attacks (such as rainbow tables, where a table of digests can be used to perform a lookup upon a particular digest), and dictionary attacks (see Section B, Part 4 - Brute Force and Dictionary Attacks).
When generating a salt, we need to ensure that the salt itself is considered cryptographically secure. This means there needs to be plenty of entropy in the generation process to ensure that a sufficiently random string of characters is generated. For generating these salts, this means that you should opt to use the likes of openssl_random_pseudo_bytes() over mt_rand() (read here for more information about randomness issues).
Salts are commonly generated on a per-user basis. This means that when storing a user's details, you will need to store their unique salt alongside their hashed password. Whilst this does not slow down attempts of cracking individual passwords, it greatly slows down trying to crack a whole table of passwords. This is because of the added inconvenience of using different salts for each password, which only enables the attacker to compute digests for a single password at a time when attempting to crack them (rather than directing the attack at the whole table at once).
Further Reading:


Risks and Challenges of Password Hashing
PKCS #5 v2.1: Password-Based Cryptography Standard

Remember to still force your users to use good passwords within your application logic. Making your users use a minimum of X characters in their password with at least one non-alphabetical character is good practice for ensuring security on their behalf.

Credits: http://www.hackforums.net/showthread.php?tid=4238146

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...