bcrypt hashing vs comparing on user login - node.js

First, I apologise for the question. I know there are a ton of similar obvious questions that ask for how to implement bcrypt in X app, but here I'm asking between 2 strategies.
I just recently implemented bcrypt into the user login. Basically:
User sends credentials to my rest api server
I bcrypt.hash the given password and compare it to the database with a simple where clause as the following:
WHERE mail = $mail and password = $my_just_hashed_password
If I find a user on the database I assume that the given password is correct. Otherwise, I reject the request.
This is almost the same as first selecting the user from the unique mail given and then bcrypt.compare the given password and the hashed one from the database.
My question is, which one is better? I know that the bcrypt hashing function is intended to be used to safely store sensible data, but why not using it to also validate users?

I will provide an alternative answer to the already approved answer, with emphasis on security...
As the email is unique, I would first find the user based on the email alone, get the password from the retrieved user, and compare these using bcrypt. If the email exists, but the password doesn't match, there may be a brute force attack in place, you may want to log these failed attempts so you can lock accounts or add a captcha perhaps to slow down/prevent the attack.
How is this different
ORIGINAL SOLUTION
Get the user from the email and hashed password
If the credentials are right, great, the user can log in. No problems here
If the user is not found, was the email or password incorrect? You can't know with this one query alone.
The potential attacker is free to continue trying to access a potential account by trying different passwords on the inputted email.
ALTERNATIVE SOLUTION
Get the user from the email alone
If the user exists, compare the hashed user password in the DB with the hash password of the inputted credentials (using bcrypt compare).
If the password is incorrect, log this in another table (USER_FAILED_LOGINS), after 3 failed attempts, add a some security measure such as a captcha.
If the password is correct, great, log the user in.
You could of course, use the original solution and just do another query if the credentials were incorrect to figure out if the email exists and implement the security measures at this point. Just be wary of these types of attacks and prevent against them. Also, make sure you are using prepared statements in your SQL.

When you use bcrypt to hash a password correctly, you append a string of at least sixteen random characters to the password and run the whole thing through bcrypt many thousands of times. It should take at least a quarter second to complete the hash algorithm. Then you store the random. string, known as a salt, along with the hashed password.
To verify a password, you extract the salt from the stored password hash, then hash the password you're verifying along with the salt. The two hashes must match.
Because of the salt, there's no way you'll ever be able to compare one hash to another and expect them to be equal.
Read this, please. https://en.m.wikipedia.org/wiki/Bcrypt#Description
Why is this so complex? Because cybercreeps. Because cybercreeps sometimes can steal the users table for a large web app. It is bad enough that they steal the usernames and emails, but we don't want them to steal the passwords too. See Adobe. See Ashley Madison.
Without the random salts, it's possible for attackers to construct lookup tables to help guess passwords from hashes: RAM is cheap these days. The random salts mean that the lookup tables are no longer feasible. You know some user will choose 654321 as a password, and we don't want attackers to be able to search all the passwords for a hashed version of that and other common passwords.
Also, we use the Blowfish cypher algorithm because it is slower than MD-5 and the various SHA hashes. That's good: we want attackers to be faced with a slow hashing algorithm, so they can't guess many thousands of passwords each second. Brute force password guessing is prohibitively time consuming with bcrypt.
Finally, it's a principle of information security to use carefully validated encryption algorithms. It's unwise to roll your own. Assume that the cybercreeps are smarter and more highly motivated than you, and you will be more secure.
Do not use the method you describe, please.

Related

Store Secure Username in Database

I would like to protect my users' username in an online service, as it may be personally identifying (e.g., an email-address), but am wondering if it's even possible...
My first inclination was to hash it (unsalted), but am worried about possible hash collisions. Not so much worried about the probability of a collision in an SHA256 32-bit hash, but more about the possibility that the class of usernames used could be just prone to collisions.
I also looked into perfect hashes, but as the users can be added dynamically, that's going to be too hard to manage.
Another option I've thought of was that (when adding the user) if there were a hash collision, I would reply to the client with a request for another hash, and repeat until there was no collision. I'd repeat this process during log-in. However, I am also wondering if this actually makes it easier for an attacker, as they'd have more feedback about what hashes were successful, and if the database were compromised, all the additional hashes would make recovering the original value easier.
I was also considering encrypting the username using the username as a password, but I'm guessing this also suffers from collisions (because each entry has a unique password--two different plain-texts with two different passwords could result in the same cipher-text), so I'm thinking it's not worth exploring this further.
I don't really want to go with a custom username (where the user has to come up with something that hasn't been taken when they sign-up), as I'm expecting the user to very rarely use the service, and are likely to forget their username.
I'm currently thinking I will just go with the first idea of hashing once, and if there is a collision, have the password decide (and hope there's no collision there too--I could put a warning when the user signs-up saying that their username/password is not allowed because it will log them in as another user perhaps /S).
Is there any non-colliding way of creating a secure form of username?
Thank you.
Assuming we are talking about emails, as there aren't many other options usable for login names.
I was also considering encrypting the username using the username as a password, but I'm guessing this also suffers from collisions (because each entry has a unique password--two different plain-texts with two different passwords could result in the same cipher-text), so I'm thinking it's not worth exploring this further.
Collisions here are the wrong thing to worry about here ...
Mandatory disclaimer: Encryption keys are not the same things as passwords. And encrypting the plainText with itself as the key is even worse.
The problem with encryption is that cipherTexts aren't searchable; i.e. you cannot verify for uniqueness, unless you decrypt all user records each time, so this just isn't sustainable - your server loads will grow exponentially with each new user record.
That's because while encryption makes use of IVs (Initialization Vector; i.e. the equivalent of salts in password hashing), which results in different cipherText even if you encrypt the same plainText twice, using the same key.
However, it is very likely that you will need to encrypt those emails, as if you need to send out password reset links, notifications, etc. - you'll need a two-way mechanism. You can't do these things with hashes, because they are one-way only.
There's a reason why every website couples its user accounts with email addresses, even if they are not the login names. :)
What you can do for login checks only, is to store a HMAC (Hash-based Message Authentication Code) of the email.
HMACs look just like regular hashes, but are actually "keyed hashes" (i.e. you would use a key while hashing, similarly to encryption). And in addition to that, nobody has managed to find collisions with the HMAC construct so far, even with the now famously insecure MD5 (still, please use a modern algorithm; at least SHA-2).
I should note that HMACs aren't nearly as strong as password hashing algorithms, so your users emails certainly won't be as strongly protected as their passwords, but it's not like there's anything else you can do about it, and it should be OK.
In summary, you'll need to have two separate cryptographic keys configured in your application - one for encryption, and one for the HMACs - and the following data stored:
userLoginLookup - HMAC of the email, using one of the two keys
userLoginMailer - cipherText of the email, using the second configured key
userPassword - a standard password hash; using bcrypt, PBKDF2 or scrypt
Note: Cryptography is always case-sensitive, so to accomodate lookups, you need to always normalize the email addresses first; i.e. make them all-lowercase or all-uppercase.
When a user attempts to login, you do a HMAC(emailInput, hmacKey) and search for a match with the userLoginLookup field in your database.
When you need to send a notification or password reset email, you decrypt the userLoginMailer.

What is the need for a strong password?

Now a days each and every website recommend or force us to use a strong password. Every website is most likely that they hash the password and store it in their database then what is the need for strong password if they are hashing the password or why is it required in first place. Is it the same with salted hashing also?
Because hackers try word lists and find out if they hash to the same value as stored in the database.
Weak passwords will be broken quickly, especially if no salt is used meaning that rainbow tables exist that allow lookups of the hash to find the password directly.
There is also the the threat of online attacks. e.g. an attacker trying every account against a web service login to see if they have password1, abc123, baseball, etc.
If you want a convenient way of having strong and unique passwords on every site you use, use something like https://lastpass.com
The reason for a strong password is because simple passwords can be more easily "unhashed" than a strong one. The larger character sets used in the password, and the longer the password it is, the harder it is to crack. Take a look at these videos as there is way too much information to discuss here:
https://www.youtube.com/watch?v=7U-RbOKanYs
https://www.youtube.com/watch?v=3NjQ9b3pgIg

How do modern websites check your password without actually storing it?

I've been told that you should not store the users password in a database, but how can I authenticate users if I cannot save their password? Is simply encrypting them enough to keep them safe?
There have been several stories in the news lately of high-profile sites that have been compromised, like LinkedIn, and I don't think such a high profile site would store plain-text passwords, so would assume they were encrypted.
Disclaimer: I've originally posted this on Quora but felt that the answer was more suited to Stack Overflow.
The method used to store and check user passwords without actually keeping the passwords is to compare the user input to the stored hash.
What is hashing?
Hashing is the process of passing data of variable length (small passwords, big passwords, binary files, whatever) through an algorithm that returns it as a set of fixed length called a hash value. Hashes only work one way. An *.img file consisting of several Mb can be hashed exactly the same as a password. (actually it's a common practice to use hashes on large files to check for their integrity; say you download a file using bittorrent, when it's complete the software hashes it and compares the hash of what you have with the hash of what you where supposed to have, if they match the download is not corrupt).
How does auth with hashes work?
When the user registers he gives a password, say pass123 that is then hashed (by any of the available hashing algorithms: sha1, sha256, etc, on this case md5) to the value 32250170a0dca92d53ec9624f336ca24 and that value is stored on database. Every time you try to login the system will hash you password in real time and compare it to the stored hash, if it matches, you're good to go. You can try an online md5 hasher here: http://md5-hash-online.waraxe.us/
What if two hashes are the same? Could a user login with a different pass?
He could! That is called a collision. Say that on a fictional hashing algorithm the value pass123 would produce the hash ec9624 and the value pass321 would produce the exact same hash, that hashing algorithm would be broken. Both common algorithms md5 and sha1 (the one LinkedIn used) are broken as collisions have been found. Being broken does not necessarily means it's unsafe.
How can you exploit collisions?
If you can generate a hash, that is the same as the hash generated by the user password you can identify to that site as the user.
Rainbow table-attacks.
Crackers quickly understood that once they had captured a table of hashed-passwords it would not be feasible to exploit passwords one by one so they devised a new attack vector. They would generate every single password in existence (aaa, aab, aac, aad, etc, etc) and store all the hashes in a database. Then they would only need to search for the stolen hash on the database with all the sequentially generated hashes (a sub-second query) and get the according password.
Salt to the rescue (and where LinkedIn failed big!)
Security is defined by the amount of time it will take for a cracker to break your password and the frequency by which you change it. With rainbow tables security drops really fast so the industry came up with salt. What if every password had a unique twist? That's salt! For every user that registers you generate a random string, say 3 characters (the industry recommends 16 chars - https://stackoverflow.com/a/18419...). Then you concatenate the user's password with your random string.
password - salt - sha1 hash
qwerty - 123 - 5cec175b165e3d5e62c9e13ce848ef6feac81bff
qwerty - 321 - b8b92ab870c50ce5fc59571dc0c77f9a4a90323c
qazwsx - abc - c6aec64efe2a25c6bc35aeea2aafb2e86ac96a0c
qazwsx - cba - 31e42c24f71dc5a453b2635e6ec57eadf03090fd
As you can see the exact same passwords, given different values of salt, generate completely different hashes. That is the purpose of salt and why LinkedIn failed big. Notice that on table you will only store the hash and the salt! Never the password!
The first thing the guys that got their hand on the LinkedIn hashes did was to sort of the hashes and see if there were matches (there were because multiple users had the same password - shame on them!) those users were the first to drop. If the pass table was salted... none of that would have happened and they would need an excruciating amount of time (and computer resources) to crack every single password. That would have given LinkedIn plenty of time to enforce a new password policy.
Hope the technical side of the answer gave insight as to how authentication works (or should work).
Really like it, when somebody askes this question, because this somebody want's to do it better. With knowing only a few important points, even well-known sites could have avoided lots of troubles.
Recently i wrote a tutorial about Hashing passwords, it uses a hopefully easy and understandable language. It allows to play around with SQL-injection, explains usage of salt and pepper and points out the need for slow key-derivation functions.

what are common username and password policy

Edit Jan 18th 2010,
Is there any symbol that should NOT be allowed to use in a password?
=========================================
Hi,
I am wondering what 'common' policy out there for username/password for creating a new account on a website.
This is currently what I have:
===========For username ==================
Length between 6 and 20 characters
Spaces are not allowed
Usernames are case sensitive
can contain lettlers, numbers, and symbols
* Uppercase letter (A-Z)
* Lowercase letter (a-z)
* Digit (0-9)
can not change after registration
===========For Password=============
6-20 chars long
can contain lettlers, numbers, and symbols
* Uppercase letter (A-Z)
* Lowercase letter (a-z)
* Digit (0-9)
* Special character (~`!##$%^&*()+=_-{}[]\|:;”’?/<>,.)
password is encrypted in the database
password can be sent to the email address when requested
Thanks
For username you can make it case sensitive but I probably wouldn't allow 'similar' matches.
For example it would be annoying to have these usernames on the site as all difference account:
Luigi
LUIGI
luIgI
LUigi
It could lead to griefing (people using similar account names to mock/harass someone). And it will just be confusing. And it causes problems when you have similar characters ilike l I i 0 o O.
I would rather use an email address as a username though since they will remember it. It is annoying having different usernames for lots of different sites. Email addresses are guaranteed to be unique :)
The password restrictions seem fine. As for that it is just a matter of how strong you want to force their passwords to be. Although, I would not send passwords through email. Email is insecure and the reset password method is preferred here.
password is encrypted in the database
password can be sent to the email
address when requested
Encrypted? No. Hashed? YES. If its hashed, you can't get the password back from the hash to send it to the user, and this is as it should be.
If the user forgets a password, you reset it with a temporary one, and email THAT to the user, so they can define a new password.
NEVER store plain-text or encrypted passwords in your database; if your software can unencrypt it, an attacker who got his hands on your database can do it, too.
You should let users change their username. What's the reason for stopping them?
Do you really need to make usernames case sensitive?
Don't just encrypt the password and send it to the user. Use salted hashes to store it, and if the user forgets it, generate a new one for them.
Don't bother with password restrictions at all, they just make it easier to see the search space of passwords people can try with a brute-force attack. Instead just use a list of passwords people shouldn't use (dictionary words, etc). If I want to use an entire Japanese novel for my password, that should be my choice. If you're storing them correctly as salted hashes, then the hashes will always be the same size anyway, regardless of how long my original password was.
Do you need username to be case sensitive?
Do not send out password when requested. Instead send them a password reset link. This way people wont complain that their account might be compromised even if some oe has access to the user DB.
1 practice that is becoming more and more common, is not to have usernames and passwords on your site, but to rely on OpenID or other Identity validation providers.
And sending user passwords in email is a horrible habit that must be stopped. if the user forgot his password, send him a random one, and ask him to change it. please, don't send him HIS password in email (as we all know, most users use the same password on many apps/sites).
Why are you enforcing a maximum limit on usernames? Does it seriously affect you, or compromise your database, if someone wants -for whatever reason- to use a particularly long character-string for their username?
If you're encrypting the password the upper limit doesn't really make sense, since the hash (whatever function you use) will produce a consistent length string more or less regardless -according to my understanding of hash functions- of length. I'd also consider running the submitted -and hashed/encrypted- password against known rainbow tables, to try and enforce strong passwords. But this may, or may not, be possible depending on the available APIs or licensing terms of such sites that feature searchable rainbow tables.
One approach would be to not place many restrictions on passwords but have a list of passwords that users can't use. Here is the list of passwords that can't be used on Twitter:
http://www.techcrunch.com/wp-content/uploads/2009/12/Twitter-banned-passwords.txt
This would filter out the weakest passwords while still giving your users to choose the passwords that they want.
Don't store it if you don't need to...
Is OpenID not an option?
If you do, make it strong...
I like the idea of using an email address as the "username," eliminating the need to store it later for communication, etc. And as for passwords, encourage "strong" ones. 6-12 characters of random-casing and numbers interspersed.
Not many good reasons to store plain text...
I would then hash the password.
I thing the policy you are using is good. But you should never ever store the password of someone in the database. I would suggest adding the username (If it could not be changed; else maybe the id) and build a hash of that. And always if you check a password you again add the username and build the checksum (sha512 is ok) and test if that is the same as the one stored in the database. Maybe it would be a nice thing to add a constant random salt. (also like the username)
You may get something like sha512("myname_password_4235329659").
That makes using rainbowtable these days almost impossible.
I think You should narrow it this way:
Use:
Email Address as Username;
Password must be strong by forcing a rule as follows:
Alphabets(in small or capital),
Numbers
Characters(#,&,*, !...)
must include at least one of these and must be 6 or more in length
Case sensitivity must be applied when signing in
Hint: Use a password generator as an assistance
A security question and answer for later recovery of username or password or
simply use birth date validation for this

Encrypting/Hashing plain text passwords in database [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Improve this question
I've inherited a web app that I've just discovered stores over 300,000 usernames/passwords in plain text in a SQL Server database. I realize that this is a Very Bad Thing™.
Knowing that I'll have to update the login and password update processes to encrypt/decrypt, and with the smallest impact on the rest of the system, what would you recommend as the best way to remove the plain text passwords from the database?
Any help is appreciated.
Edit: Sorry if I was unclear, I meant to ask what would be your procedure to encrypt/hash the passwords, not specific encryption/hashing methods.
Should I just:
Make a backup of the DB
Update login/update password code
After hours, go through all records in the users table hashing the password and replacing each one
Test to ensure users can still login/update passwords
I guess my concern is more from the sheer number of users so I want to make sure I'm doing this correctly.
EDIT (2016): use Argon2, scrypt, bcrypt, or PBKDF2, in that order of preference. Use as large a slowdown factor as is feasible for your situation. Use a vetted existing implementation. Make sure you use a proper salt (although the libraries you're using should be making sure of this for you).
When you hash the passwords use DO NOT USE PLAIN MD5.
Use PBKDF2, which basically means using a random salt to prevent rainbow table attacks, and iterating (re-hashing) enough times to slow the hashing down - not so much that your application takes too long, but enough that an attacker brute-forcing a large number of different password will notice
From the document:
Iterate at least 1000 times, preferably more - time your implementation to see how many iterations are feasible for you.
8 bytes (64 bits) of salt are sufficient, and the random doesn't need to be secure (the salt is unencrypted, we're not worried someone will guess it).
A good way to apply the salt when hashing is to use HMAC with your favorite hash algorithm, using the password as the HMAC key and the salt as the text to hash (see this section of the document).
Example implementation in Python, using SHA-256 as the secure hash:
EDIT: as mentioned by Eli Collins this is not a PBKDF2 implementation. You should prefer implementations which stick to the standard, such as PassLib.
from hashlib import sha256
from hmac import HMAC
import random
def random_bytes(num_bytes):
return "".join(chr(random.randrange(256)) for i in xrange(num_bytes))
def pbkdf_sha256(password, salt, iterations):
result = password
for i in xrange(iterations):
result = HMAC(result, salt, sha256).digest() # use HMAC to apply the salt
return result
NUM_ITERATIONS = 5000
def hash_password(plain_password):
salt = random_bytes(8) # 64 bits
hashed_password = pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)
# return the salt and hashed password, encoded in base64 and split with ","
return salt.encode("base64").strip() + "," + hashed_password.encode("base64").strip()
def check_password(saved_password_entry, plain_password):
salt, hashed_password = saved_password_entry.split(",")
salt = salt.decode("base64")
hashed_password = hashed_password.decode("base64")
return hashed_password == pbkdf_sha256(plain_password, salt, NUM_ITERATIONS)
password_entry = hash_password("mysecret")
print password_entry # will print, for example: 8Y1ZO8Y1pi4=,r7Acg5iRiZ/x4QwFLhPMjASESxesoIcdJRSDkqWYfaA=
check_password(password_entry, "mysecret") # returns True
The basic strategy is to use a key derivation function to "hash" the password with some salt. The salt and the hash result are stored in the database. When a user inputs a password, the salt and their input are hashed in the same way and compared to the stored value. If they match, the user is authenticated.
The devil is in the details. First, a lot depends on the hash algorithm that is chosen. A key derivation algorithm like PBKDF2, based on a hash-based message authentication code, makes it "computationally infeasible" to find an input (in this case, a password) that will produce a given output (what an attacker has found in the database).
A pre-computed dictionary attack uses pre-computed index, or dictionary, from hash outputs to passwords. Hashing is slow (or it's supposed to be, anyway), so the attacker hashes all of the likely passwords once, and stores the result indexed in such a way that given a hash, he can lookup a corresponding password. This is a classic tradeoff of space for time. Since password lists can be huge, there are ways to tune the tradeoff (like rainbow tables), so that an attacker can give up a little speed to save a lot of space.
Pre-computation attacks are thwarted by using "cryptographic salt". This is some data that is hashed with the password. It doesn't need to be a secret, it just needs to be unpredictable for a given password. For each value of salt, an attacker would need a new dictionary. If you use one byte of salt, an attacker needs 256 copies of their dictionary, each generated with a different salt. First, he'd use the salt to lookup the correct dictionary, then he'd use the hash output to look up a usable password. But what if you add 4 bytes? Now he needs 4 billion copies of the the dictionary. By using a large enough salt, a dictionary attack is precluded. In practice, 8 to 16 bytes of data from a cryptographic quality random number generator makes a good salt.
With pre-computation off the table, an attacker has compute the hash on each attempt. How long it takes to find a password now depends entirely on how long it takes to hash a candidate. This time is increased by iteration of the hash function. The number iterations is generally a parameter of the key derivation function; today, a lot of mobile devices use 10,000 to 20,000 iterations, while a server might use 100,000 or more. (The bcrypt algorithm uses the term "cost factor", which is a logarithmic measure of the time required.)
I would imagine you will have to add a column to the database for the encrypted password then run a batch job over all records which gets the current password, encrypts it (as others have mentiond a hash like md5 is pretty standard edit: but should not be used on its own - see other answers for good discussions), stores it in the new column and checks it all happened smoothly.
Then you will need to update your front-end to hash the user-entered password at login time and verify that vs the stored hash, rather than checking plaintext-vs-plaintext.
It would seem prudent to me to leave both columns in place for a little while to ensure that nothing hinky has gone on, before eventually removing the plaintext passwords all-together.
Don't forget also that anytime the password is acessed the code will have to change, such as password change / reminder requests. You will of course lose the ability to email out forgotten passwords, but this is no bad thing. You will have to use a password reset system instead.
Edit:
One final point, you might want to consider avoiding the error I made on my first attempt at a test-bed secure login website:
When processing the user password, consider where the hashing takes place. In my case the hash was calculated by the PHP code running on the webserver, but the password was transmitted to the page from the user's machine in plaintext! This was ok(ish) in the environment I was working in, as it was inside an https system anyway (uni network). But, in the real world I imagine you would want to hash the password before it leaves the user system, using javascript etc. and then transmit the hash to your site.
Follow Xan's advice of keeping the current password column around for a while so if things go bad, you can rollback quick-n-easy.
As far as encrypting your passwords:
use a salt
use a hash algorithm that's meant for passwords (ie., - it's slow)
See Thomas Ptacek's Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes for some details.
I think you should do the following:
Create a new column called HASHED_PASSWORD or something similar.
Modify your code so that it checks for both columns.
Gradually migrate passwords from the non-hashed table to the hashed one. For example, when a user logs in, migrate his or her password automatically to the hashed column and remove the unhashed version. All newly registered users will have hashed passwords.
After hours, you can run a script which migrates n users a time
When you have no more unhashed passwords left, you can remove your old password column (you may not be able to do so, depends on the database you are using). Also, you can remove the code to handle the old passwords.
You're done!
As the others mentioned, you don't want to decrypt if you can help it. Standard best practice is to encrypt using a one-way hash, and then when the user logs in hash their password to compare it.
Otherwise you'll have to use a strong encryption to encrypt and then decrypt. I'd only recommend this if the political reasons are strong (for example, your users are used to being able to call the help desk to retrieve their password, and you have strong pressure from the top not to change that). In that case, I'd start with encryption and then start building a business case to move to hashing.
For authentication purposes you should avoid storing the passwords using reversible encryption, i.e. you should only store the password hash and check the hash of the user-supplied password against the hash you have stored. However, that approach has a drawback: it's vulnerable to rainbow table attacks, should an attacker get hold of your password store database.
What you should do is store the hashes of a pre-chosen (and secret) salt value + the password. I.e., concatenate the salt and the password, hash the result, and store this hash. When authenticating, do the same - concatenate your salt value and the user-supplied password, hash, then check for equality. This makes rainbow table attacks unfeasible.
Of course, if the user send passwords across the network (for example, if you're working on a web or client-server application), then you should not send the password in clear text across, so instead of storing hash(salt + password) you should store and check against hash(salt + hash(password)), and have your client pre-hash the user-supplied password and send that one across the network. This protects your user's password as well, should the user (as many do) re-use the same password for multiple purposes.
Encrypt using something like MD5, encode it as a hex string
You need a salt; in your case, the username can be used as the salt (it has to be unique, the username should be the most unique value available ;-)
use the old password field to store the MD5, but tag the MD5 (i.e.g "MD5:687A878....") so that old (plain text) and new (MD5) passwords can co-exist
change the login procedure to verify against the MD5 if there is an MD5, and against the plain password otherwise
change the "change password" and "new user" functions to create MD5'ed passwords only
now you can run the conversion batch job, which might take as long as needed
after the conversion has been run, remove the legacy-support
Step 1: Add encrypted field to database
Step 2: Change code so that when password is changed, it updates both fields but logging in still uses old field.
Step 3: Run script to populate all the new fields.
Step 4: Change code so that logging in uses new field and changing passwords stops updating old field.
Step 5: Remove unencrypted passwords from database.
This should allow you to accomplish the changeover without interruption to the end user.
Also:
Something I would do is name the new database field something that is completely unrelated to password like "LastSessionID" or something similarly boring. Then instead of removing the password field, just populate with hashes of random data. Then, if your database ever gets compromised, they can spend all the time they want trying to decrypt the "password" field.
This may not actually accomplish anything, but it's fun thinking about someone sitting there trying to figure out worthless information
As with all security decisions, there are tradeoffs. If you hash the password, which is probably your easiest move, you can't offer a password retrieval function that returns the original password, nor can your staff look up a person's password in order to access their account.
You can use symmetric encryption, which has its own security drawbacks. (If your server is compromised, the symmetric encryption key may be compromised also).
You can use public-key encryption, and run password retrieval/customer service on a separate machine which stores the private key in isolation from the web application. This is the most secure, but requires a two-machine architecture, and probably a firewall in between.
MD5 and SHA1 have shown a bit of weakness (two words can result in the same hash) so using SHA256-SHA512 / iterative hashes is recommended to hash the password.
I would write a small program in the language that the application is written in that goes and generates a random salt that is unique for each user and a hash of the password. The reason I tend to use the same language as the verification is that different crypto libraries can do things slightly differently (i.e. padding) so using the same library to generate the hash and verify it eliminates that risk. This application could also then verify the login after the table has been updated if you want as it knows the plain text password still.
Don't use MD5/SHA1
Generate a good random salt (many crypto libraries have a salt generator)
An iterative hash algorithm as orip recommended
Ensure that the passwords are not transmitted in plain text over the wire
I would like to suggest one improvement to the great python example posted by Orip. I would redefine the random_bytes function to be:
def random_bytes(num_bytes):
return os.urandom(num_bytes)
Of course, you would have to import the os module. The os.urandom function provides a random sequence of bytes that can be safely used in cryptographic applications. See the reference help of this function for further details.
To hash the password you can use the HashBytes function. Returns a varbinary, so you'd have to create a new column and then delete the old varchar one.
Like
ALTER TABLE users ADD COLUMN hashedPassword varbinary(max);
ALTER TABLE users ADD COLUMN salt char(10);
--Generate random salts and update the column, after that
UPDATE users SET hashedPassword = HashBytes('SHA1',salt + '|' + password);
Then you modify the code to validate the password, using a query like
SELECT count(*) from users WHERE hashedPassword =
HashBytes('SHA1',salt + '|' + <password>)
where <password> is the value entered by the user.
I'm not a security expert, but i htink the current recommendation is to use bcrypt/blowfish or a SHA-2 variant, not MD5 / SHA1.
Probably you need to think in terms of a full security audit, too

Resources