Understanding Hash Functions and Keeping Passwords Safe

Understanding Hash Functions and Keeping Passwords Safe

Tutorial Details
  • Language: PHP
  • Difficulty: Intermediate
  • Estimated Completion Time: 30min

From time to time, servers and databases are stolen or compromised. With this in mind, it is important to ensure that some crucial user data, such as passwords, can not be recovered. Today, we are going to learn the basics behind hashing and what it takes to protect passwords in your web applications.

Republished Tutorial

Every few weeks, we revisit some of our reader's favorite posts from throughout the history of the site. This tutorial was first published in January of 2011.


1. Disclaimer

Cryptology is a sufficiently complicated subject, and I am by no means an expert. There is constant research happening in this area, in many universities and security agencies.

In this article, I will try to keep things as simple as possible, while presenting to you a reasonably secure method of storing passwords in a web application.


2. What Does “Hashing” Do?

Hashing converts a piece of data (either small or large), into a relatively short piece of data such as a string or an integer.

This is accomplished by using a one-way hash function. “One-way” means that it is very difficult (or practically impossible) to reverse it.

A common example of a hash function is md5(), which is quite popular in many different languages and systems.

$data = "Hello World";
$hash = md5($data);
echo $hash; // b10a8db164e0754105b7a99be72e3fe5

With md5(), the result will always be a 32 character long string. But, it contains only hexadecimal characters; technically it can also be represented as a 128-bit (16 byte) integer. You may md5() much longer strings and data, and you will still end up with a hash of this length. This fact alone might give you a hint as to why this is considered a “one-way” function.


3. Using a Hash Function for Storing Passwords

The usual process during a user registration:

  • User fills out registration form, including the password field.
  • The web script stores all of the information into a database.
  • However, the password is run through a hash function, before being stored.
  • The original version of the password has not been stored anywhere, so it is technically discarded.

And the login process:

  • User enters username (or e-mail) and password.
  • The script runs the password through the same hashing function.
  • The script finds the user record from the database, and reads the stored hashed password.
  • Both of these values are compared, and the access is granted if they match.

Once we decide on a decent method for hashing the password, we are going to implement this process later in this article.

Note that the original password has never been stored anywhere. If the database is stolen, the user logins can not be compromised, right? Well, the answer is “it depends.” Let’s look at some potential problems.


4. Problem #1: Hash Collision

A hash “collision” occurs when two different data inputs generate the same resulting hash. The likelihood of this happening depends on which function you use.

How can this be exploited?

As an example, I have seen some older scripts which used crc32() to hash passwords. This function generates a 32-bit integer as the result. This means there are only 2^32 (i.e. 4,294,967,296) possible outcomes.

Let’s hash a password:

echo crc32('supersecretpassword');
// outputs: 323322056

Now, let’s assume the role of a person who has stolen a database, and has the hash value. We may not be able to convert 323322056 into ‘supersecretpassword’, however, we can figure out another password that will convert to the same hash value, with a simple script:

set_time_limit(0);
$i = 0;
while (true) {

	if (crc32(base64_encode($i)) == 323322056) {
		echo base64_encode($i);
		exit;
	}

	$i++;
}

This may run for a while, though, eventually, it should return a string. We can use this returned string — instead of ‘supersecretpassword’ — and it will allow us to successfully login into that person’s account.

For example, after running this exact script for a few moments on my computer, I was given ‘MTIxMjY5MTAwNg==‘. Let’s test it out:

echo crc32('supersecretpassword');
// outputs: 323322056

echo crc32('MTIxMjY5MTAwNg==');
// outputs: 323322056

How can this be prevented?

Nowadays, a powerful home PC can be used to run a hash function almost a billion times per second. So we need a hash function that has a very big range.

For example, md5() might be suitable, as it generates 128-bit hashes. This translates into 340,282,366,920,938,463,463,374,607,431,768,211,456 possible outcomes. It is impossible to run through so many iterations to find collisions. However some people have still found ways to do this (see here).

Sha1

Sha1() is a better alternative, and it generates an even longer 160-bit hash value.


5. Problem #2: Rainbow Tables

Even if we fix the collision issue, we’re still not safe yet.

A rainbow table is built by calculating the hash values of commonly used words and their combinations.

These tables can have as many as millions or even billions of rows.

For example, you can go through a dictionary, and generate hash values for every word. You can also start combining words together, and generate hashes for those too. That is not all; you can even start adding digits before/after/between words, and store them in the table as well.

Considering how cheap storage is nowadays, gigantic Rainbow Tables can be produced and used.

How can this be exploited?

Let’s imagine that a large database is stolen, along with 10 million password hashes. It is fairly easy to search the rainbow table for each of them. Not all of them will be found, certainly, but, nonetheless…some of them will!

How can this be prevented?

We can try adding a “salt”. Here is an example:

$password = "easypassword";

// this may be found in a rainbow table
// because the password contains 2 common words
echo sha1($password); // 6c94d3b42518febd4ad747801d50a8972022f956

// use bunch of random characters, and it can be longer than this
$salt = "f#@V)Hu^%Hgfds";

// this will NOT be found in any pre-built rainbow table
echo sha1($salt . $password); // cd56a16759623378628c0d9336af69b74d9d71a5

What we basically do is concatenate the “salt” string with the passwords before hashing them. The resulting string obviously will not be on any pre-built rainbow table. But, we’re still not safe just yet!


6. Problem #3: Rainbow Tables (again)

Remember that a Rainbow Table may be created from scratch, after the database has been stolen.

How can this be exploited?

Even if a salt was used, this may have been stolen along with the database. All they have to do is generate a new Rainbow Table from scratch, but this time they concatenate the salt to every word that they are putting in the table.

For example, in a generic Rainbow Table, “easypassword” may exist. But in this new Rainbow Table, they have “f#@V)Hu^%Hgfdseasypassword” as well. When they run all of the 10 million stolen salted hashes against this table, they will again be able to find some matches.

How can this be prevented?

We can use a “unique salt” instead, which changes for each user.

A candidate for this kind of salt is the user’s id value from the database:

$hash = sha1($user_id . $password);

This is assuming that a user’s id number never changes, which is typically the case.

We may also generate a random string for each user and use that as the unique salt. But we would need to ensure that we store that in the user record somewhere.

// generates a 22 character long random string
function unique_salt() {

	return substr(sha1(mt_rand()),0,22);
}

$unique_salt = unique_salt();

$hash = sha1($unique_salt . $password);

// and save the $unique_salt with the user record
// ...

This method protects us against Rainbow Tables, because now every single password has been salted with a different value. The attacker would have to generate 10 million separate Rainbow Tables, which would be completely impractical.


7. Problem #4: Hash Speed

Most hashing functions have been designed with speed in mind, because they are often used to calculate checksum values for large data sets and files, to check for data integrity.

How can this be exploited?

As I mentioned before, a modern PC with powerful GPU’s (yes, video cards) can be programmed to calculate roughly a billion hashes per second. This way, they can use a brute force attack to try every single possible password.

You may think that requiring a minimum 8 character long password might keep it safe from a brute force attack, but let’s determine if that is, indeed, the case:

  • If the password can contain lowercase, uppercase letters and number, that is 62 (26+26+10) possible characters.
  • An 8 character long string has 62^8 possible versions. That is a little over 218 trillion.
  • At a rate of 1 billion hashes per second, that can be solved in about 60 hours.

And for 6 character long passwords, which is also quite common, it would take under 1 minute.

Feel free to require 9 or 10 character long passwords, however you might start annoying some of your users.

How can this be prevented?

Use a slower hash function.

Imagine that you use a hash function that can only run 1 million times per second on the same hardware, instead of 1 billion times per second. It would then take the attacker 1000 times longer to brute force a hash. 60 hours would turn into nearly 7 years!

One way to do that would be to implement it yourself:

function myhash($password, $unique_salt) {

	$salt = "f#@V)Hu^%Hgfds";
	$hash = sha1($unique_salt . $password);

	// make it take 1000 times longer
	for ($i = 0; $i < 1000; $i++) {
		$hash = sha1($hash);
	}

	return $hash;
}

Or you may use an algorithm that supports a "cost parameter," such as BLOWFISH. In PHP, this can be done using the crypt() function.

function myhash($password, $unique_salt) {

	// the salt for blowfish should be 22 characters long

	return crypt($password, '$2a$10$'.$unique_salt);

}

The second parameter to the crypt() function contains some values separated by the dollar sign ($).

The first value is '$2a', which indicates that we will be using the BLOWFISH algorithm.

The second value, '$10' in this case, is the "cost parameter". This is the base-2 logarithm of how many iterations it will run (10 => 2^10 = 1024 iterations.) This number can range between 04 and 31.

Let's run an example:

function myhash($password, $unique_salt) {
	return crypt($password, '$2a$10$'.$unique_salt);

}
function unique_salt() {
	return substr(sha1(mt_rand()),0,22);
}
$password = "verysecret";

echo myhash($password, unique_salt());
// result: $2a$10$dfda807d832b094184faeu1elwhtR2Xhtuvs3R9J1nfRGBCudCCzC

The resulting hash contains the algorithm ($2a), the cost parameter ($10), and the 22 character salt that was used. The rest of it is the calculated hash. Let's run a test:

// assume this was pulled from the database
$hash = '$2a$10$dfda807d832b094184faeu1elwhtR2Xhtuvs3R9J1nfRGBCudCCzC';

// assume this is the password the user entered to log back in
$password = "verysecret";

if (check_password($hash, $password)) {
	echo "Access Granted!";
} else {
	echo "Access Denied!";
}
function check_password($hash, $password) {

	// first 29 characters include algorithm, cost and salt
	// let's call it $full_salt
	$full_salt = substr($hash, 0, 29);

	// run the hash function on $password
	$new_hash = crypt($password, $full_salt);

	// returns true or false
	return ($hash == $new_hash);
}

When we run this, we see "Access Granted!"


8. Putting it Together

With all of the above in mind, let's write a utility class based on what we learned so far:

class PassHash {

	// blowfish
	private static $algo = '$2a';

	// cost parameter
	private static $cost = '$10';
	// mainly for internal use
	public static function unique_salt() {
		return substr(sha1(mt_rand()),0,22);
	}

	// this will be used to generate a hash
	public static function hash($password) {

		return crypt($password,
					self::$algo .
					self::$cost .
					'$' . self::unique_salt());

	}
	// this will be used to compare a password against a hash
	public static function check_password($hash, $password) {

		$full_salt = substr($hash, 0, 29);

		$new_hash = crypt($password, $full_salt);

		return ($hash == $new_hash);

	}

}

Here is the usage during user registration:

// include the class
require ("PassHash.php");

// read all form input from $_POST
// ...

// do your regular form validation stuff
// ...

// hash the password
$pass_hash = PassHash::hash($_POST['password']);

// store all user info in the DB, excluding $_POST['password']
// store $pass_hash instead
// ...

And here is the usage during a user login process:

// include the class
require ("PassHash.php");

// read all form input from $_POST
// ...

// fetch the user record based on $_POST['username']  or similar
// ...

// check the password the user tried to login with
if (PassHash::check_password($user['pass_hash'], $_POST['password']) {
	// grant access
	// ...
} else {
	// deny access
	// ...
}


9. A Note on Blowfish Availability

The Blowfish algorithm may not be implemented in all systems, even though it is quite popular by now. You may check your system with this code:

if (CRYPT_BLOWFISH == 1) {
	echo "Yes";
} else {
	echo "No";
}

However, as of PHP 5.3, you do not need to worry; PHP ships with this implementation built in.


Conclusion

This method of hashing passwords should be solid enough for most web applications. That said, don't forget: you can also require that your members use stronger passwords, by enforcing minimum lengths, mixed characters, digits & special characters.

A question to you, reader: how do you hash your passwords? Can you recommend any improvements over this implementation?

Tags: security
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.jotlab.com/ Richard S

    Great article spelling out the need and precautions people can take to add a greater level of security to their database tables. I’ve written a CakePHP row based salt plugin/behaviour that easily slots into CakePHP and works almost seamlessly with Cake’s Auth component. Might be of use to anyone if they want an easy way to guard against the threat of rainbow tables:

    https://github.com/voidet/grey_tables &
    http://www.jotlab.com/2010/04/18/cakephp-rainbow-table-protection-behaviour/

  • http://www.drivvedwebbyra.se Fredrik

    We always use the md5 function with a salt with great results… its easy and pretty hard to crack.

    • http://www.phpandstuff.com/ Burak
      Author

      If the attacker knows your salt, your method is very vulnerable to both dictionary and brute force attacks, as I explained in the article.

      You should use random salts, and slower hash functions than md5().

      • http://nathansweet.me Nathan Sweet

        Out of curiosity how would generating a random salt work? How would you replicate it? Random for each user?

      • Moby

        One way to retrieve random salts is to append the random character string to the hash.

        $random_number = random(); //424890719842314
        $password = $_POST['password']; //correct horse battery staple
        $hashed_and_salted = sha1($random_number.$password); //yayhashnonsense
        $stored_value = $random_number.$hashed_and_salted; //424890719842314yayhashnonsense

        Then you just substring the random number off of the front of the value and hash away. Your random function has to generate a fixed length string however, but that’s not difficult.

  • http://www.jsxtech.com Jaspal Singh

    Excellent tutorial on Password Hashing.
    Thanks for sharing.

  • Abdulaziz

    This was very helpful and easy to understand .
    Thanks ..

  • Félix

    I don’t understand the check_password function, why don’t you compare with the
    stored hash in the BBDD? Something like this:

    function check_password(password, nickname) {
    //get user from nickname user = User.objects.get(nickname=nickname)
    return user.hash_stored == hash(password)

    Btw in your function check_password I suppose that in order to calculate again the
    hash I’d have to do it with the cost parameter, something like this:
    // this will be used to compare a password against a hash
    public static function check_password($hash, $password) {

    $new_hash = hash($password);

    return ($hash == $new_hash);

    • http://www.phpandstuff.com/ Burak
      Author

      The check_password method is comparing a given hash to a given password. It’s a generic tool. We don’t pass usernames to it.

      Also, the cost parameter is already in the stored hash, along with the salt. So, in my code $full_salt has them both.

  • Robin J

    After reading this ill make this function that ill use now and not just singel hash my pws

    function CryptMe($String){
    $HashOne = crc32($String);
    $HashTwo = md5($HashOne);
    $FinalString = $HashOne . $HashTwo;
    $HashThree = sha1($FinalString);
    Return $HashThree;

    • Tom

      Well, that could look clever but I am sorry to say that it is not. You are far better off using a function like this:

      function CryptMe($String) {
      $StaticSalt = “some-long-string-containing-random-characters”;
      $HashThree = sha1($String . $StaticSalt);
      return $HashThree;

      The reason is that even if it might be difficult for an attacker to guess what hashes you have used and how you have combined them, compared to guessing the static salt it should still be considered a picnic. You will increase the security much more by just adding one more character to the static salt than messing about with how the strings are combined.

      • Another Tom

        You’re right that his clever idea is bad, but your suggestion is also bad. Don’t use sha1 for passwords. Don’t prepend static text to your password before hashing to try to make an HMAC. A static salt is no good. You’re better off just using the blowfish example in the article.

      • Tom

        @Another Tom: For some reason I could not reply to your reply. Hence replying to my own.

        Yes! You are absolutely right that my example is not optimal. My intent was however not to create the perfect crypt function, but rather point out how to easily improve Robin J’s initial function. I just chose SHA1 because it was better than the MD5 he also used. I agree that blowfish would be the way to go since ensuring that each hash value “costs” a lot to calculate would severely limit an attacker’s possibility of success.

        I do however not agree that a static salt is bad. (It should of cause be combined with a dynamic salt -not shown in my example). The reason is that if a hacker gets hold of only the DB or only the source code, you will still stand a pretty god chance of protecting the passwords. I understand that a hacker will obviously try to get both but since they are not stored in the same location, you can still cling to the hope that he fails to obtain at least one of them. The DB would be useless for him without the static salt in the source code.

        But again, my point above was just to show how to easily improve the method. A (reasonably long) static salt string would be much better than just using a bunch of secondary hash methods after one another.

  • http://jakswebdesign.com Adam

    Dynamic hashes would really kick the crap out of hackers trying to get passwords from stolen hashes.

    For example, lots of user systems store a last login timestamp in the same row as the user. It’d be a virtually impossible task for a hacker to obtain passwords using stolen hashes if the password hashes were dynamic. e.g. salt the password with the timestamp from the last login (obviously you’ll have to find some other way of salting at time 0) and then check that against the hash – if there’s a match, then update the database with the password salted with the current login timestamp (stored in the database as last login) and voila a dynamic hash for a non-changing password.

    There’s a hundred ways of making the hash dynamic while always keeping the same password, using a last login timestamp is just one that I thought of because it’s commonly stored in user systems. Let a user pick “boobs” as their password (http://theoatmeal.com/comics/shopping_cart)….who cares? With dynamic hashes it’ll be impossible to crack!!

    • http://www.offerscript.net Codez

      The only problem about this, is simply it would be a mere headache to anyone using such a system. This is not something that would be good usability for mass production projects.

    • Another Tom

      This is just as effective than using a random salt. If somebody stole your database, they’d have the salt in the table. Salts aren’t meant to be a secret in most password algorithms. And the enemy of security is clever and complex hacks. Generate a random salt, use that like in the Blowfish example. Any clever modifications are going to lower the effective security.

  • CG

    Could you provide a quick snippet of code? Does it go in the unique_salt function?

  • http://www.allthewebsites.org Webmaster – Allthewebsites.org

    According to Wikipedia, security flaws were identified in SHA-1.
    SHA-2 is recommended.

    In PHP, it is simple to create SHA-2 salt using hash() .

  • Tgr

    “For example, md5() might be suitable, as it generates 128-bit hashes. This translates into 340,282,366,920,938,463,463,374,607,431,768,211,456 possible outcomes. It is impossible to run through so many iterations to find collisions. However some people have still found ways to do this (see here).”

    This is a misunderstanding. The link describes a collision attack against MD5. A collision attack means that the attacker can generate two strings which have the same hash. Neither of the strings are predetermined. This is dangerous for some applications (such as digital signatures), but totally irrelevant when you are securing passwords. There, the attacker has a fixed string (the hash he stole from your database) and needs to find another string which hashes to the same value. This is called a preimage attack; no useful preimage attack is known against MD5.

    • http://www.offerscript.net Codez

      Actually you can use a databse where the phrase has already been typed out with md5 hashing applied to it, this is saved in a data base so you just use a form and post the data to the db in a select and you can grab the decryption of the said match, granted someone has to have typed it out first but it does exist and right now is the only feasible way to get past md5 hashing.

  • Rob

    Salts should be random, not the user_id. Why? Because user_id’s are generally allocated sequentially, and so will be small (for some definition of “small”). This make it much easier for the attacker to pre-calculate hashes than it need to be.

  • Nathan

    The only problem I can see with this is if the hacker gets the database then they have the unique salt. If you store it as a key in the user table, then its easy to find. But if they get to the database, then they might get to the code as well. I know this is a best practice, but I don’t think you can completely prevent everything. Especially if they get the code.

  • http://www.sagie.es cesar

    Great tut, thanks for sharing!

    I’ve tested it locally and it works wonderfully, however I can’t use it on my shared server because it uses php 5.2.

    Does anybody know how to use this with php 5.2?

  • Cosmin

    Great tutorial Burak, as always. ;)
    I’m working with CodeIgniter at the moment, and I thought that I should try to implement this class into CI.
    I’ve created a new librabry called Pass_hash and in the users_model I’ve done something like:

    function validate()
    {
    $this->load->library(‘Pass_hash’);

    $this->db->where(‘username’, $this->input->post(‘username’));
    $this->db->where(‘password’, $this->pass_hash->hash($this->input->post(‘password’)));

    $query = $this->db->get(‘users’);

    if($query->num_rows == 1)
    {
    return true;
    }

    function create_user()
    {
    $this->load->library(‘Pass_hash’);

    $username = $this->session->userdata(‘username’);
    $datestring = “%d/%m/%Y – %h:%i:%s”;
    $time = time();

    $new_user = array(
    ‘first_name’ => $this->input->post(‘first_name’),
    ‘last_name’ => $this->input->post(‘last_name’),
    ‘username’ => $this->input->post(‘username’),
    ‘password’ => $this->pass_hash->hash($this->input->post(‘password’)),
    ‘email_address’ => $this->input->post(‘email_address’),
    ‘date’ => mdate($datestring,$time)
    );

    $insert = $this->db->insert(‘users’,$new_user);
    return $insert;
    }

    But it doesn’t work…
    And I can’t figure out where to put the check_password function.

    • enya

      would be interested to know if you found a solution, am also hoping to incorporate this in CodeIgniter!

    • http://ericmagnuson.me Eric Magnuson

      It looks like you’re missing a closing bracket ‘}’ for your validate function.

      There’s an excellent little Bcrypt class for PHP over on Github. It’s very easy to plug into CI using a helper. For instance, make a file called login_helper.php, drop the above class into this file, and make sure to load it in the autoload.php config file. Then, you can call the class functions whenever you are doing authentication within a controller. If you need more specifics, let me know.

  • dante

    thanks!

  • http://www.lonebench.com Alexander

    Thanks for the advice. I’m currently creating an app that rests on hash-work, and security. This will really help me out! :)

  • Simon Davies

    I might be stupid but can someone please explain the :: within the function calls etc as I am used to suing the-> way so for EG:

    $pass_hash = PassHash::hash($_POST['password']);

    I would use $pass_hash = PassHash->hash…… as the :: throws me an error.

    Many Thanks

    • http://roadha.us haliphax

      When you see those double-colons in PHP, it means that you are referencing a static method or property that belongs to the class. PassHash->hash does not work, because there is no OBJECT named PassHash — there is only a CLASS named PassHash. The “hash” method has been defined as static, meaning that it does not belong to an object, but to the class itself. You could declare a new PassHash() object and then try myObject->hash, but it’s unnecessary.

  • Bill

    What about when you have to use http_auth?

    $data = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST']);
    $A1 = md5($data['username'] . ‘:’ . $auth_realm . ‘:’ . ‘saved_password’);
    $A2 = md5($_SERVER['REQUEST_METHOD'].’:’.$data['uri']);
    $valid_response = md5($A1.’:’.$data['nonce'].’:’.$data['nc'].’:’.$data['cnonce'].’:’.$data['qop'].’:’.$A2);

    As you can see above, it requires the text version of the saved password. What is the best way to handle this? Saving the password as a one way hash prevents decoding the password. Am I back to encrypting nd salting to save the password? Then I can decrypt it when I need to do http_auth logins against a database.

    • http://roadha.us haliphax

      You’re pretty much hosed if you’re needing to pass actual credentials to a separate application/service. This is generally where web services and token-based credentials come into play.

  • http://jaze.com.au Mike Causer

    Thanks for sharing this excellent tutorial.

  • Sickboy

    Well, I guess that this is an extra security layer, but its far from being 100% safe. Dont belive that with this measure your passwords will be 100% safe.

    You dont even need to know the hash, the salt or anything related. Just have a huge list of random usernames and passwords as well as a big proxy list and you can easily bruteforce your login form in your site without needing salts or hash.

    Even if you have bruteforce protection, firewall, etc, if a hacker gets access to your database and files, the can download your whole site, remove bruteforce protection and run your site in localhost and bruteforce the login form with a simple 20 lines php script….

    With bruteforce you always get the real password…. and Its the prefered method by every n00b.

    • http://roadha.us haliphax

      You will never be 100% safe — that’s the way computer security works. As far as brute forcing — I don’t think you understand the sheer volume of attempts that would be necessary for a salted, one-way-hashed table of passwords.

      If an attacker got your database AND your code, they wouldn’t need to brute force anything; they would just hard-code authorization and be on their merry way.

  • http://www.sigmawebtechnologies.com Larry Levenson

    This was a great help, Burak. Thank you! I’m creating a web application for a museum, and this was an easy way to implement some hash functions to keep their password safe.

  • worm

    Hi, I have a proposition concerning the problem #4, the hash speed, and it is about your first solution, the custom myhash() function, instead of using a loop that hashs the password 1000 times, why do not use the sleep() function for 1 or 2 seconds.
    Because in the loop solution, you would need to repeat the loop in the code for inserting or updating a user’s password, not in the sleep() solution, where the code would appears only in one place, the authentification process.

    • http://www.safesparky.com registered dog tags

      Worm,

      Using sleep doesn’t actually make the password harder to crack. Someone trying to crack your hash isn’t going to use sleep() in their code.

      if your password was: asdf
      and it hashed into: H3x.3

      Using sleep it would still be the same hash. But if you rehashed it then it might become dk2.3

      Now someone trying to crack your hash can’t just run a hash algorithm once, but twice. That slows them down. Do it 1000 times and it slows them down a lot. They can’t bypass rehashing like you can just by pass a delay.

      (I know the hashes aren’t real)

      • worm

        registered dog tags,

        It depends in what problem you are trying to resolve, from what i understood, the problem #4 speaks about the brute force problem, it’s when someone uses a script against your login form trying a big number of combinations of caracters, in order to fall on your password, so, one solution would be to increase the time of the login process, hence the 1000 times loop, but what i noticed is you can achieve the same thing using a simpler manner, the sleep() function ;

        note : so here is not a question about the strength of the hash, i assume that you are satisfied with the use of a salt to make the hash stronger

      • AZ

        worm -

        The brute force attack in question is -not- a matter of hitting the original login page with a large number of login attempts. (That’s only -one- type of brute force attack.)

        The brute force attack in question starts with stealing a database containing password info (see the preamble to the article; also see many recent news articles — it’s a common tactic).

        So: (1) I steal your password info, (2) I see on your site that your site requires 8 characters minimum for the password, (3) I hash all 8-character combinations in under 3 days, (4) I find matches with all the users who didn’t bother to use more than 8 characters.

        If -you- hash a password 1,000 times to arrive at the final hash, then the -attacker- has to also, slowing him/her down by 3 orders of magnitude. As noted in the article, that turns 3-day effort into a 7-year effort.

        As one part of a multi-faceted approach, it goes a long way toward making attacks much more difficult (not impossible — just much more difficult).

  • http://www.safesparky.com jeff

    also, bcrypt handles stuff like this for you.

    Can download it at:
    http://www.openwall.com/phpass/

    More info at:
    http://stackoverflow.com/questions/1581610/how-can-i-store-my-users-passwords-safely
    (look at the best answer -the first comment- for details on how to implement)

  • Dhaneesh R

    Hi

    I have a doubt, please help, can we write a custom function that will generate the salt from username or password

    and the calculated password is same for the same username or password, so it will not be needed to save the salt in db

    is it secure?

  • kankaro

    I have copy the above class created by the author of this article an use it to one of my projects.. the class perfectly function.. thanks for the great tuts Mr. Burak Guzel …

  • Kevork A

    Poison your hashes. Not only will it throw off the hacker (because the length of the final hash will not be expected, ie. 32,40,128)

    And it makes it literally impossible to reverse the hash unless the hacker also found your hashing function to see where the poisoning occurs.

    One more obstacle to throw at them.

  • http://blog.mostof.it/ Ochronus

    Nice summary, I’ve also written a bit about this stuff here: http://blog.mostof.it/secure-password-storage-a-myth/ – regarding Sony’s late password scandals.

  • Jens Sierens

    Very neat tutorial. I just got interested in this matter. But there is one thing I can’t figure out, or I’m missing something very obvious here.

    If the user passwords are stored as a hash in the database, how do they implement a lost password retrieval system? Since a hash algorithm cannot retrieve the original input out of a hash string and the only reference to the user’s password in the database is the hash.

    It seems to me that an uncyphered copy of the password is stored as a backup but that is a huge security risk and makes the hashing proces completely redundant in the first place.

    • Saleh

      To : Jens Sierens
      They give you a reset password link, or a new generated apssword on your email, they don’t give you your old password … it depends of who we are talking about here.

    • http://roadha.us haliphax

      Never, ever, EVER, EVER, *EVER* send sensitive information (credentials included) over e-mail. EVER. FOR ANY REASON. E-mail is NOT secure in any way (unless you get into encrypted e-mail, which is another story).

      In fact, when using those reset/activation links, make sure they expire, too.

  • https://twitter.com/#!/arcesino Ignacio Arces

    Great tips for securing webapps. I think another good technique against brute force attacks is to limit the number of login attempts in a determined period of time and temporarily block the account if the limit is reached. I’ve seen this technique in a lot of web applications. Also, if the problem is that the database was stolen; why not to consider to encrypt the username as well. I think it will made the attackers job so much difficult!

  • Iman

    In the solution offered for problem #3 , If the attacker can access to the database so he has access to salt values for each user and it is east for him to make rainbow table for each user with the user specific salt .

  • Iman

    Where the number 29 came from in the putting it together example?
    Thank you.

    • Iman

      I could find the answer:
      It is because there are 22 characters for salt and 7 characters for algorith and cost and sum will be 29.

  • Max2474

    I have always had a few doubts about hashing though most likely just due to lack of understanding…
    1. if a database is stolen, then surely all of a users information (except the password) is readily available for an attacker to view..they wouldn’t need a password in the first place….?
    2. If an attack is performed on the site itself, then no matter how much hashing is done, 1 guess of the right password using rainbow tables will allow access.

    That said, here are my thoughts for the security of the site I am currently developing.

    1. Take the chosen users password and split it – probably in half. If you know there is a minimum of 6 letters in the password, split it 6 times (the 6th time being the remainder of the password)

    E.G. “pass” +”word”, or even “p”, “a”, “s”, “s”,”w”,”o”,”r”,”d”.

    2. Use different hashes and salts with each part.
    $part1=sha1(userid+”p”);
    $part2=md5(datejoined+”a”);
    $part3=whirlpool(mysitehash+”s”)
    etc
    etc

    3. save each of these in the database, and then combine them when checking login.
    if (password $part1+part2

    I am also considering making members to have a number code as well (like banks often do) as this essentially means that an attacker has two passwords to crack. (my site does need to be extra secure :) )

    Would appreciate your thoughts!

    • Another Tom

      No. Clever solutions are usually bad in security. Use a standardized approach like the Blowfish example above. You’re also adding needless complexity, which usually also means less security.

      To answer your other points, they must be addressed in other parts of a comprehensive security policy.
      1. Any sensitive information in the database should be encrypted. SSL/TLS takes care of encryption of data in transit. You have to take care of encryption of data at rest.
      2. A site should limit the number of incorrect password attempts before locking out the account or beginning throttling. That makes online brute forcing infeasible.

  • peter

    very usefull explanation.
    i have one comment to all those who are scared for brute force attacks.
    I block user-accounts for 15 minutes after 3 repetetive failed logins, sent the accountholder and myself an email and log these failed login actions into my todo-table:)
    Afterwards, if needed i investigate serverlogfiles.

    Tnaks for this article!!

  • Kaidefix

    Great article, thanks a lot!

    Not that it would make any difference, but
    return substr(sha1(mt_rand()),0,22);
    should be
    return substr(sha1(mt_rand()),0,21);

    its 22 characters for Blowfish

  • Kaidefix

    well, I think I was wrong..
    Looks like
    return substr(sha1(mt_rand()),0,22);
    somehow removes a “.” in the hash…

  • Kaidefix

    Re my previious posts: My stupid mistake about the substring function….

    Still there is something I don’t understand:

    Encryption of password “PASSWORD”:

    $2a$10$0b703e086de47039da8d5f
    returns hash:
    $2a$10$0b703e086de47039da8d5eAjS3naC/63DhnzFKYONOKAapNn/FxPu

    $2a$10$0b703e086de47039da8d5e
    returns hash:
    $2a$10$0b703e086de47039da8d5eAjS3naC/63DhnzFKYONOKAapNn/FxPu

    Why is that? The last character is different “f” and “e”.
    If I use a random character, sometimes it returns the same hash, sometimes not:

    $2a$10$0b703e086de47039da8d55
    $2a$10$0b703e086de47039da8d5uze2RjrqB5M6OZpONoHIPUrzwr3Iqh96

  • Md. Abul Fazal

    hash function is the good protection of database.

  • Anonymous

    Haha, this timing. Well it’s great to see Envato admitting they messed up, letting the users know EXACTLY what happened and giving us all some free subs to tuts+ ;)

  • http://www.thecodingbox.com bluepicaso

    just to put some more lights,
    http://krebsonsecurity.com/2012/06/how-companies-can-beef-up-password-security/
    also view comments

    i am a developer and gave a few hours reading things after linkedin’s issue.

    thank you

  • http://ahmadmain.com Ahmad Amin

    It’s this a coincidences or was it intended just as tutsplus came out with a statement saying that some of it’s password is stored in clear-text?

    • http://www.jeffrey-way.com Jeffrey Way

      Just reiterating that Nettuts+ promotes best practices…even if some of our sister-sites haven’t. :/

      • http://www.leihai.com/ Stephen Curtis

        You cannot absolve yourself of responsibility from your revenue stream. If you knew they had bad security practices and took money through the site, it’s your fault. If you didn’t know because you didn’t care or think to ask, it’s still your fault.

      • http://www.jeffrey-way.com Jeffrey Way

        Hey Stephen – I get what you’re saying, but, again, the team who manages/writes for Nettuts+ has nothing to do with the team who works on Tuts+ Premium. Envato as a company is responsible for the breach, which includes everyone. But, to be specific, Nettuts+ has always promoted best practices, like the ones outlined in this article.

      • http://www.leihai.com/ Stephen Curtis

        Is the net.tutsplus.com team going to review the measures tutsplus premium is taking to make sure this doesn’t happen in the future? If they won’t allow it, will you break your relationship with tutsplus premium to protect your users?

      • http://ahmadmain.com Ahmad Amin

        I don’t what tutsplus reason was for keeping password in clear-text but it’s fairly common for some applications to save password raw to give users in the future in needed rather than creating a new one.

        Even Google seems to be doing it (I don’t know if they are encrypting (not hash) and then saving it) for Google Apps as and admin you can go in and see user’s passwords.

      • Devin Dombrowski

        Jeffrey,

        I have been a long time follower of yours and I trust your integrity and decisions. Just tell me why my password wasn’t secure. I’m sure that you are frustrated with this situation as well but I must say that this is a very unsettling situation.

      • http://www.jeffrey-way.com Jeffrey Way

        Hey Devin –

        It’s a complicated situation, and one that I don’t have any real involvement in – so it’s tough to answer your question. Just be sure to pay attention to notes.envato.com for more information.

  • Jinxed

    Concerning current state of tutsplus security, you guys are very funny. :)

  • http://laranz.in Lawrence77

    Good timing, use it for tutsplus+ :P

  • TylerF

    You didn’t need to revisit it, you could have just sent it in an email directly to the CEO. Give him a heads up that clear text passwords aren’t exactly the most secure.

  • http://www.leihai.com/ Stephen Curtis

    At least you guys have a good sense of humor…. tutsplus += 1;

  • http://whiteboard.is Jonathan Cutrell

    A good reminder.

    I’ll second using BCrypt, btw – http://bcrypt.sourceforge.net/ – Super easy to use for ruby folks especially.

  • Matt

    Is this the definition of irony?

  • http://whiteboard.is Jonathan

    Oops.

    Clearly Blowfish IS bcrypt.

    Ignore my quick post. :)

  • Safaali

    Thanks Burak,

    I have worked with many types of dehashing softwares included RainbowTables method.
    I can assure all who read this comment that with current available CPUs ( or multi-CPU / GPU ) at the market it is very very very time consuming to dehash a 9 character password containing numeric-lowecase-upercase-specialCharacters like this: hU8y%tg$b even with RainbowTables.

    Another good practice I have experienced is not to use same salt for all encryption jobs, make it random for each hash.

    Thanks again for this tut.

  • Andrew

    Hey, great article!

    I have been using hash_hmac(‘sha1′, $password, $pass_salt); any thoughts on this?

  • Cameron Phillips

    Hahaha slightly ironic