Jump to content

sha1(md5())


Kryptix

Recommended Posts

Is there anything wrong in doing this?

 

I currently have 100,000+ users all with their passwords hashed in md5(). I want to secure it a bit by simply hashing all of their existing hashes to sha1() and then check their password matches the sha1(md5()).

 

Is there any reason why I shouldn't do this?

Link to comment
Share on other sites

Also, does anyone know of a good article explaining how password salts work and why it's a good idea to use them? If the salt is stored in the same row as the password I can't see how it makes it more secure?

Link to comment
Share on other sites

In theory, your idea should work.  I would try it, but first I would backup my database table -> FULLY!!!  AS for better protection, well, you will get different answers on that on.  Some say md5 is enough, some say sha1 is enough, others say not to mix the two.  I say, if it works, and adds a little more buffer zone, then go for it.

Link to comment
Share on other sites

I don't see why sha1(md5()) won't work.  And you can apply it to your existing database of users.  Make double sure it's correct though, and keep a backup of the original md5 passwords (eg on a usb stick) in case it goes pear shaped.  There's a few things it'll achieve

 

1.  sha1(md5()) takes longer to compute than md5(), making brute force cracking take a bit longer.

2.  Rainbow tables typically use md5() only, so rainbow tables won't work.

3.  Out of the box cracking tools typically won't use sha1(md5()), so someone without programming knowledge will have more difficulty programming a cracking attack correctly.

 

You will still benefit from unique salts for each user though, regardless of whether you use sha1(md5()) or md5().

Link to comment
Share on other sites

Multiple hashes won't benefit enough to justify using them.

 

It's a ton of extra CPU power for a little extra protection.

 

A random salt, scrambled into the hash itself is your best bet. I'll reply a little later with my methods of random salting.

Link to comment
Share on other sites

From an attackers point of view - If they see the data/encrypted password, how are they to know it is SHA1(md5())? They may just spend their attempts just trying to crack md5(); or sha1(); instead of the combination. Rainbow tables will be skewed by adding in the sha1();.

 

Salt is defiantly the way to go if you really do want a security blanket though. I have personally used sha1(md5($pass)); and must say it worked but it was never used in a large environment.

 

Blah blah backup etc..

 

Good luck!

Link to comment
Share on other sites

Sorry, been away from a computer for a while.

 

The problem here is, if someone makes a SHA1 rainbow table up to 32 characters. If they have your database of password hashes, it doesn't take long to realize all of your passwords reverse to 32 character hashes. From there, its a matter of comparing the new hashes to an md5 rainbow table and your passwords are gone.

 

This MAY seem difficult, but if the attacker has several sock-puppet accounts in the database, and can link plain text passwords to their respective hashes, the patterns become pretty easy to reverse.

 

You COULD use a less common hashing method, but that's security through obscurity, and I'm not a fan.

 

Instead, use a random salt, and insert it into random spots within your hash. This makes the salt nearly impossible to extract

 

<?php

// Unique key - I use this to add further entropy between sripts, or multiple
// implementations within a script. It will further be used in hash_hmac, which
// forces an attacker to use a custom rainbow table for your site EVEN IF they
// manage to extract the salts, and learn how the salt is mixed in pre-hash
$key = 'ANY RANDOM STUFF CAN GO HERE. CAN BE LONG, CAN BE SHORT. I LIKE AT LEAST 20 CHARS';
// I plan on splitting the salt in half, and storing it in variable spots within
// the hash. This will tell us where the second split happens
$split = 6;


$password = 'FOOBAR';

$hash = make_hash( $password, $key, $split );

// The cool thing here is EVERY time you refresh the page you'll end up with a VERY
// different hash. If two users enter the same password, the chances of the hash being
// the same is miniscule.
echo "The hash of $password is $hash<br />";

if ( compare_hash($password, $hash, $key, $split ) )
echo "Passwords match on comparison";
else
echo "Something went wrong!";


function make_hash( $pass, $key, $split ) {

$passLength = strlen( $pass ); // Get the length of the password
// If the password is really long, we dont want to push the salt
// to the far end of the hash.
while ( $passLength > 30 ) $passLength -= 30;
$salt = uuid();
$hash = hash_hmac( 'sha256', $salt.$pass, $key );
// Mix in the salt with the hash
return substr($hash,0,$passLength).substr($salt,0,$passLength).
		substr($hash,$passLength,$split).substr($salt,$passLength).
		substr($hash,$passLength+$split-1);
// Returns FirstPartHash.FirstPartSalt.SecondPartHash.LastPartSalt.LastPartHash
// The salt is mixed into the hash based on the password length, making it VERY
// hard to reverse.

}

function compare_hash( $pass, $compare, $key, $split ) {

$passLength = strlen( $pass );
while ( $passLength > 30 ) $passLength -= 30;
// Extract the salt from the $compare hash.
$salt = substr($compare,$passLength,$passLength).substr($compare,$passLength*2+$split,32-$passLength);
// Once we have the salt, build the hash and see if it compares.
$hash = hash_hmac( 'sha256', $salt.$pass, $key );
$hash = substr($hash,0,$passLength).substr($salt,0,$passLength).
		substr($hash,$passLength,$split).substr($salt,$passLength).
		substr($hash,$passLength+$split-1);
if( $hash === $compare )
	return TRUE;
else
	return FALSE;

}

// RF4122 UUID Generator v4 - I prefer this over uniqid() due to a constant 128bit
// output, and no use of the time function. It's pseudo-unique, but will work very
// well for our application.
function uuid( ) {
return sprintf( '%04x%04x%04x%04x%04x%04x%04x%04x',
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
        mt_rand( 0, 0x0fff ) | 0x4000,
        mt_rand( 0, 0x3fff ) | 0x8000,
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) );
}

?>

 

In the end, if you base your security system around a post in a forum, you're relying on a complete stranger to protect your script. Understand what's going on and be careful what advice you implement.

Link to comment
Share on other sites

I'd like to add that the only good adding sha1() does is to serve as a salt shaker. The hashing it provides is worthless. Why?

 

md5() will always spit out a 32-character hexadecimal string. sha1() will always spit out a 40-character hexadecimal string.

By using MD5 first you take the infinite domain of inputs and reduce it to a finite (though very large) output. While SHA1 is capable of many more output strings (1.5E48), you now limit it to MD5's 3.4E38. Add onto that hash collisions and incomplete ranges and you get an even smaller pool of output strings. Add onto that the fact that the algorithms are different: now you overlap inputs so that they have the same MD5+SHA1 hash even though the independent MD5 or SHA1 hashes differ.

 

The one advantage offered is that rainbow tables of MD5+SHA1 are less common than ones for each alone. However somebody could simply assemble one, or as xyph mentioned, have a SHA1 table of/up to 32 characters of input.

 

A salt is a minimum requirement, but once you have that, throwing more hashing algorithms on top doesn't do you much good.

 

* Unless the actual algorithms somehow preclude this from happening.

Link to comment
Share on other sites

The problem here is, if someone makes a SHA1 rainbow table up to 32 characters. If they have your database of password hashes, it doesn't take long to realize all of your passwords reverse to 32 character hashes. From there, its a matter of comparing the new hashes to an md5 rainbow table and your passwords are gone.

 

This MAY seem difficult, but if the attacker has several sock-puppet accounts in the database, and can link plain text passwords to their respective hashes, the patterns become pretty easy to reverse.

 

MD5 hashes have 128 bits = 16 bytes.  The number of possible combinations is therefore 2^128, which is a lot.  Assuming we need 0 space for indexing (which is not true), this still requires 2^128 * 16 bytes to complete the rainbow table.  To give an idea of the space required:

 

2^10 * 16 bytes = 16kb

2^20 * 16 bytes = 16mb

2^30 * 16 bytes = 16gb

2^40 * 16 bytes = 16 tb

2^50 * 16 bytes = 16 pb

2^60 * 16 bytes = 16 eb

2^70 * 16 bytes = 16 zb

...

2^128 * 16 bytes = ?

 

Well, you get the idea.  We don't even have a name for the amount of storage required to make a rainbow table for the sha1() of all 32 character md5 hashes.  Such a rainbow table will not exist for the likely lifetime of the application.  A rainbow table could be generated for just the md5 hashes in the exisiting md5 rainbow table, but I am not aware of any such table existing.

 

Using a unique per-user salt is still a good idea, even though a rainbow table for all 32 character inputs is not possible.

 

requinix, I don't agree with you there.  Although entropy is not increased by applying sha1(), the difficulty of an attack is increased.  The whole point of a rainbow table is that it is pre-generated and can be shared between attacks.  md5() has rainbow tables because it is commonly used, but I'm not aware of any rainbow tables for sha1(md5()).

Link to comment
Share on other sites

requinix, I don't agree with you there.  Although entropy is not increased by applying sha1(), the difficulty of an attack is increased.  The whole point of a rainbow table is that it is pre-generated and can be shared between attacks.  md5() has rainbow tables because it is commonly used, but I'm not aware of any rainbow tables for sha1(md5()).

It does make an attack harder but it's still more feasible than if the system used a proper salt. Rainbow tables are pretty much out, I'll give you that, but a dictionary attack would still work.

I was focusing more on the theoretical aspects: applying both sha1() and md5() is worse than just sha1() in terms of entropy. Back in reality, it will take longer to find a password, but that's because sha1() acts as a salt in the sense that it takes a discoverable input and creates a less discoverable output. (Yes, "salt" is the wrong term, I know. It's more like "brine".)

 

Above all I wanted to make my post sound scary. Adding another hashing algorithm is like obscurity: it gets you a bit further but isn't something to rely upon.

Link to comment
Share on other sites

Good point. However, it doesn't address how someone with several known password->hash combinations could easily reverse engineer that method, and attempt to start attacking hashes.

 

A basic salting approach with a strong algo is arguably harder to reverse than sha1(md5())

A complex solution like mine, where a random salt is stored at variable places within the hash itself, is nearly impossible to reverse - even with several known password->salted-hash combinations. It's very light to process, as it's mostly shifting strings around, with a single hash call to compare, and a couple to generate a new salted-hash

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

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.   Restore formatting

  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...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.