views:

317

answers:

11

User System and Passwords: I was looking through MD5 stuff, and I am wondering what is the normal/good practice for passwords. Right now, I think people super encrypt the passwords and store the hashes. If so, how does password checking work? I just have the input password go through the encryption process again and then check the hash with the stored one, correct?

This question may contradict the above, but should my salt ever be a randomly generated value? If so, when may it be useful?

Edit: Other than passwords, in a user system, what else should be encrypted as a good practice? Do they encrypt usernames or anything else?

2nd Edit: What is a one-way hash? I mean, technically, can I not reverse engineer my source code? Maybe this is a bad question because I do not know much about one-way hashing.

+1  A: 

Never store the password in a reversible way, always use One-Way-Hashes. Checking works by encrypting the entered password and checking the two hashes against each other.

Bobby
A: 

"Correct?"


If you need a binary answer:

 1
Betamoo
Haha, good one :)
Doug
-1, should've been a comment.
ceejayoz
A binary answer is not that bad!
Betamoo
A: 

You should store a hash of your password instead of the actual readable string, also consider using "Salting" for extra security

arclight
+8  A: 

First you create a salt.

Note examples are written in PHP

// Setup a salt, this isn't "random" but it doesn't really have to be
$salt = sha1(microtime());

Then salt the password

// First we hash the password, then XOR it with the salt hashing the result
$hash = sha1(sha1($password) ^ $salt);

Store the $hash and $salt in the database.

When the user enters a password compare it to the hash

if(sha1(sha1($entered_password) ^ $salt) == $hash)
    // Correct password

Never store passwords in a reversible format. Also I would advise against using MD5 as a hash.

Edit: Other than passwords, in a user system, what else should be encrypted as a good practice? Do they encrypt usernames or anything else?

Passwords aren't encrypted, they are hashed. Picture a hash (very simplistic) as something that takes a number and multiplies it by ten. Say I want to hash the number 30. I would say 30*10 and get 300 as my "hash" for 30. Note that you cannot derive 30 from 300 without knowing how the hash function works.

That's a very simplistic "hash" and if you know it always multiplies by ten then you could easily reverse it. Now take a look at the SHA1 hash function. It's much more complicated. It can't simply be reversed.

You will find that rarely is anything except the password hashed, and nothing is encrypted. The amount of overhead you would have with encrypting your database would be enormous.

I suppose you could apply a similar salt / hash pattern to the username, but then you have pitfalls. What if you want to use that username somewhere in your code? What if you want to check to make sure it's unique to the table?

2nd Edit: What is a one-way hash? I mean, technically, can I not reverse engineer my source code? Maybe this is a bad question because I do not know much about one-way hashing.

See above (or click here). A one way hash is just that. One way mapping. A => B and nothing else. B !=> A, and A can't be anything except B.

Someone mentioned the performance of an XOR operation. While I feel performance is largely negligible I ran a quick test.

function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

Now run

$start_time = $this->microtime_float();

for($i = 0; $i < 100000; $i++)
{
 $sha = sha1(sha1(microtime()) . sha1(microtime()));
}

$end_time = $this->microtime_float();

echo "1000 in " . ($end_time-$start_time) . " for CAT\n";


$start_time = $this->microtime_float();

for($i = 0; $i < 100000; $i++)
{
 $sha = sha1(sha1(microtime()) ^ sha1(microtime()));
}

$end_time = $this->microtime_float();

echo "1000 in " . ($end_time-$start_time) . " for XOR\n";

Repeat as much as you want. The initial writeup uses the error log and I got the following results:

1000 in 0.468002796173 XOR
1000 in 0.465842008591 XOR
1000 in 0.466115951538 XOR
1000 in 0.498080968857 CAT
1000 in 0.506876945496 CAT
1000 in 0.500174045563 CAT
Josh K
Salting it and then storing the salt in the database doesn't really help - someone could easily find the salt _and_ the hash if they were to gain database access. In my experience, the best way to do this is to salt the password using a combination of other info about the user that can't change (their primary key, their username, etc), so that the password is salted but someone gaining DB access wouldn't know how.
Cam
What does the carrot `^` mean?
Doug
@Doug: It's a `XOR` bitwise operator.
Josh K
@incrediman: you call it *experience*, I call it *security by obscurity*. Storing the salt is no problem, since its purpose is to help against pre-calculated hash attacks. For a cracked database (where the attacker gets hash + salt) you rely on the security of your algorithm.
tanascius
@incrediman: True, but unless they also had access to the code they would have to find out how I'm using the salt. On top of that they still would need access to the password – which they still don't have. If you used the primary key they would have access to that too.
Josh K
I thought a salt was used as apart of the encryption process to create a hash and not just glued right behind the hash?
Doug
@Doug: It is used in the *hashing* process to create a hash. You do need the salt to derive the hash, which is why you store it.
Josh K
Thanks for clearing that up.
Doug
@Doug: I tried to answer your other questions as well.
Josh K
@Josh K: This is a little over-the-top: `$hash = sha1(sha1($password) ^ $salt);`, `$hash = sha1($password.$salt);` would work just as well in practice (and faster). Also, the only reason I can think of to `sha1(microtime())` is to constrain it to something that can fit in your column, and `substr(str(microtime()), 0, 160)` would work equally well (and faster), although I doubt `microtime()` gives a > 160 digit number anyway. Of course, you might be intentionally making it slow (so it would take more time to crack), but the performance should at least be mentioned.
Brendan Long
@Brendan: Performance is negligible. Really. Microtime doesn't return over 160 chars, adding the subtring / str is useless. `XOR` is a consistent operation. *In fact I went so far as to run a quick test on this* [link](http://gist.github.com/438162) and the the `XOR` operation is faster. Granted no by much (0.04 sec *per 100,000 hashes*), but faster. I invite you to try this yourself.
Josh K
@Brenden: Come to think of it I have to question someone who thinks that performing *three separate string operations* (`substr`, `str`, `.`) is faster then a bitwise operator. That's just plain silly.
Josh K
Can anyone comment on any differences in security between appending/prepending your salt to the secret, and XORing the salt with the secret?
Frank Farmer
@Josh K, I didn't mean the bitwise operator, that's obviously faster. I meant how you're hashing the password and salt, then hashing them again. I just used the string concatenation operator because I don't have a clue what `XOR` ing a string and an int would do. As for performance, doing a `substr`, `str`, `.` is much faster than `sha1()`, and as you mentioned, the `substr()`, and `str()` aren't really needed. I gave that as an example of a much faster way of fitting the number in a specific space (although I realized afterwards that you would want to truncate the beginning, not the end).
Brendan Long
@Frank Farmer: There's probably no practical difference. I just used concatenation because the idea of `XOR` ing a string makes no sense to me. What my comment was about was hashing the password and salt twice, which is unnecessary (unless you want hashing to be slower, which is sometimes useful).
Brendan Long
-1 as your answers to both edits are incorrect; a [cryptographic hash](http://en.wikipedia.org/wiki/Cryptographic_hash_function) is not the same as a [general hash](http://en.wikipedia.org/wiki/Hash_function) (so, for our context, multiplying by 10 is *not* a hash). Cryptographic hashes must be (among other things) [computationally one-way](http://en.wikipedia.org/wiki/One-way_function) - this is not the same as the [mathematical definition](http://mathworld.wolfram.com/Many-to-One.html) of one-way.
BlueRaja - Danny Pflughoeft
A hash can never been reversed! If you want to get back the original value, you need to bruteforce. See a hash as a unique summery for the given input. (the are more than one inputs possible for the same summery (hash)).
VDVLeon
@VDV: I never said a hash can be reversed.
Josh K
@BlueRaja: Forgive me for dumbing down the definition of a cryptographic hash.
Josh K
@Brendan: I want a small column size yet still have the salt available so I can make them unique. If you want to quibble over the performance hit you take storing a string (and not hashing it) and storing the hash that's your business. 100000 hashes in half a second (and that is calling `sha1()` three times each hash). There is no performance problem.
Josh K
@Frank Farmer: There is no difference. I simply prefer to `XOR`.
Josh K
@Josh K, You're completely missing my point. Hashing multiple times is not only twice as slow, it's also pointless. You may be right that performance isn't a concern, but why waste CPU cycles when the simpler way is also faster? Also, while writing a quick benchmark to show this, I realized that `microtime()` isn't the best way to do this. Using `$salt = rand(0, 4294967295);` is 3x faster, 40x less space (4 byte `UNSIGNED INT` vs 160 byte `CHAR`), and just as good (since the salt doesn't need to be cryptographically secure anyway). Example: http://pastebin.com/ryjAbWT0
Brendan Long
+1  A: 

Standard practice with passwords is to not store the original password anywhere. Unix passwords used to be encrypted with "crypt" that would use a random salt. The salt itself was stored in the first two characters of the encrypted password. When the user entered his password, the system would use the two characters of the encrypted password as a salt to encrypt the entered password, and if the encrypted result was the same as the stored encrypted password, it was a match. Similar things can be done with MD5 passwords.

That's why good sites will never email you your password, but instead will reset your password to a one-time only value - because they don't know your password.

To expand on this a little bit: an MD5 hash is a one-way function - if you hash the same value you get the same hash, but you can't take the hash and somehow turn it into the value. There is a small but finite chance that two values will produce the same hash (the chance gets higher the larger the initial strings are or the smaller the hash is), but they choose the hash algorithms to make the chances that two strings people would choose as passwords would hash to the same value almost infinitesimal. You can think of a one way hash as being like a meat grinder - you can look at the meat that comes out of your meat grinder and see if it's cow, lamb or pig, but you can't pass it through the other way and get back a cow.

Because of this, nobody can recover your password because it's never stored anywhere on their system, only a hash of it.

Paul Tomblin
Could you shed more light on that last part about never e-mailing you your password?
Doug
@Doug: Never emailing means "Never email". Don't email a password and user credentials. Many places actually send passwords through email, which is a bad thing.
S.Lott
@S.Lott: That's not actually what I was asking. I understood the "never e-mailing your password" part, but how would you "reset your password to a one-time only value"?
Doug
@Paul Tomblin: Okay, so this is what I am thinking. If they request for a password reset, then I would just create a random (with @$! symbols) value and pass it through the encryption process to store the hash and then send them the random value for them to login and change their password?
Doug
@Doug, that would certainly work, although you should probably use some other authentication system (like a security question) before resetting the password in case somebody has hijacked their email. Security questions are not as secure as banks seem to think they are, but we don't really have much better.
Paul Tomblin
A: 

There are many variations on this theme.

For good information, please read this: http://en.wikipedia.org/wiki/Digest_access_authentication.

Then read this: http://tools.ietf.org/html/rfc2617

Generally: You only store a digest. Never a password.

Following RFC2617, you should store a digest of username, realm and password.

The client ("agent") takes username, password, realm, etc., and creates a digest, which it sends to your server.

Your server, based on username, looks up it's version of the digest.

If their digest == the digest saved in the server, you agree on the password (and everything else).

If their digest != the digest saved in the server, you disagree on the password (or something else). Which means they didn't get the realm or username right, or they didn't get the nonce right, or something else went wrong. They can't be trusted.

The full RFC2617 includes other data to compute a digest of other stuff and the password digest just to be absolutely sure that the client is doing the responding.

S.Lott
A: 

The reason you store a hashed password is that if someone manages to get hold of the data in your user table, they still can't log in. A one-way-hash can't be decrypted (not even by the person who encrypted it!) so it is very difficult to use the hashed password for anything.

Because you can't possibly decrypt the password in the database, you need to take the password that has been entered and repeat the same process to hash it and then compare the hashed values to find a match. Because of this, your salt can't really be totally random as you will end up getting different results.

On top of this, you don't really want a password to be transmitted in its unencrypted form, which is why login-pages are normally HTTPS pages.

Sohnee
A one way hash can be cracked easily - this is why salting is in there. Dictioary attackes against a known hash mechanism (like md5) are fast thanks to hugh precomputed dictionaries.
TomTom
@TomTom: While a hash can be cracked, it's not "easy". It takes time and resources and success depends on the "strength" of the password.
S.Lott
@Sohne: The salt can be as random as you want. Just store it in the database.
Josh K
@Josh K: How can it be random though? If it were to be random, how could I re-create the same hash (is hash the right term?) to check against the database?
Doug
Because you store the salt. See my example. Random salt is generated when the password is stored *the first time*. Subsequent login attempts use the salt that got stored.
Josh K
Ahh, I see. It depends on how you use the salt then.
Doug
Exctly. The idea of Salt is that the hash of "password" is not the same for 2 users, otherwise I can run a simple dictionary attack as I can precompute the hashes.
TomTom
@Doug: Exactly.
Josh K
@JoshK - you aren't storing in the same table in a field called "Salt" are you?
Sohnee
@Sohnee: No, it's called "trees I like."
Josh K
@Josh K: So there's no problem with storing it in the same table with a field called salt right?
Doug
@Doug: There shouldn't be unless you have a weak salt.
Josh K
A: 

2nd Edit: What is a one-way hash? I mean, technically, can I not reverse engineer my source code? Maybe this is a bad question because I do not know much about one-way hashing.

"One way" in cryptography means "hard to invert." Simply put, it means that if I give you sha1(password), you cannot find password in any reasonable amount of time.

This is called computationally one-way. Many people confuse this for another definition of one-way (from mathematics) meaning "not one-to-one", which does not apply here.

BlueRaja - Danny Pflughoeft
A: 

I strongly advise against the use of MD5. For further information, read the Wikipedia section [MD5] Security. I recommend to use the SHA-1 hashing algorithm instead.

Marius Schulz
A: 

Avoid using MD5 hashes if you can, as it's pretty flawed.

Alternatives are SHA1, or even better SHA256 or SHA512.

Damien Dennehy
+1  A: 

This question may contradict the above, but should my salt ever be a randomly generated value? If so, when may it be useful?

Salts should be random. Their sole use is to make brute-force attacks on hashes much more expensive. Something called a "rainbow table" (which is a fancy name for a database where someone hashed a whole bunch of possible passwords in advance, and lets you look up passwords if you know the hash) makes it possible to take unsalted password hashes and turn them into passwords in a fraction of a second in many cases.

A moderately sized salt can increase the complexity of a precomputed brute-force attack exponentially. For every single bit of random data in your salt, you double the time required for a precomputed brute force attack. For every unique salt value in your database, the attacker has to start over when attacking the password protected by that salt.

If you had 1kB of random salt for every user's password, precomputed hashes would be out the window. You wouldn't affect the amount of time it would take to brute-force a single user's password though.

One way you can make the brute-force attacker's life harder is by making the hashing process computationally intensive (e.g. 5000 rounds of sha1(salt+sha1(salt+sha1(salt+password)))). You only have to do that for every login attempt. The attacker has to do it for every salt + password combination they want to guess. You have to decide if this is something that is worthwhile for your needs. The answer is probably no.

Edit: Other than passwords, in a user system, what else should be encrypted as a good practice? Do they encrypt usernames or anything else?

I'm paranoid, but I would say any information that you the site owner doesn't need while the user isn't logged in should be encrypted with a derivative of the user's password. That way attackers don't have access because you don't have access.

For an online order processing system, for example, you might need their mailing address, their name and most recent order unencrypted, but their order history and favorite color might be encrypted with their account password.

Note that if you do this, and they lose their password, the protected information is lost also.

2nd Edit: What is a one-way hash? I mean, technically, can I not reverse engineer my source code? Maybe this is a bad question because I do not know much about one-way hashing.

A hash is a method for systematically throwing away information. Say you start with a string, and produce "srflcdos" by throwing away all but about every fourth character. The text I "hashed" could be: "spear if fish lies calmly. don't sit!", or it could be: "supercalifragilisticexpialidotious". There is no way to prove either way.

Cryptographic hashes do a lot more mixing and other transformations along with the throwing away, to make them more secure for small amounts of input data, and to avoid leaking any facts at all about the input data. As an example of an insecure hash, if you know that whenever the input contains the letter A, the 12 bit of the hash is 1, then you are exposing information about the original text, and the result is not a cryptographically secure hash.

The principle is that you can't reverse engineer a process if between each transformation you throw away information vital to reversing the previous transformation. An MD5sum produces 128 bits of output, regardless of whether you put in 1 bit or 12 petabytes of information. You obviously can't compress 12 petabytes into 128 bits, so information is clearly being thrown away in the process of computing the hash.

Slartibartfast