I've read about the concepts presented in these two questions:
I think I've come up with an implementation that sort of combines the concepts presented in both questions, while possibly providing several layers of security.
- Is this implementation relatively secure compared to most others?
- Have I shot myself in the foot by chaining multiple functions in this fashion?
- Is there a better way to take advantage of current 'approved', 'proven', and memory-expensive algorithms?
- How can I improve this implementation?
- Is there a point where the salt and pepper values are large enough to make cracking the hashes more difficult by requiring more memory and/or processing?
- Man in the middle attacks
- ARP spoofing, DNS hijacking, and acting as HTTP proxy
- Terminating SSL connection on attacker’s side and sending HTTP traffic to user
- Plain text password being handled by server code
- Rainbow tables and birthday problem
- Unproven algorithms
- Quick and efficient computation of hashes
- Really long credentials
- Lack of suitable entropy
- Authentication database dump results in disclosure of salts and peppers
- Plain text storage of email
- Never send plaintext password over the wire, and utilize SSL only across site
- Salt and hash the password before sending it to the server
- User unique peppers are applied on the server
- Hashing chain ends with PBKDF2, an algorithm vetted by RSA
- Hashing chain utilizes bcrypt, as it requires 4kB of memory
- Any input string to bcrypt is hashed with SHA-512 to keep the number of input characters under the maximum
- Salt and peppers will be long, random strings
- Store private salts and peppers on another service only accessible from internal API
- Store hashes of email addresses as well as passwords on servers that handle public traffic
SHA-512, bcrypt, PBKDF2
email_pepper, bcrypt_pepper, pbkdf2_pepper
- User enters email address and password in a web form, and submits them to log in.
- Email address and Password string is hashed using PBKDF2 (separately) with public_salt, and the two resulting 1024-bit keys (email_key, password_key) are POSTed to the server over SSL.
- Server hashes email_key again with PBKDF2 using email_pepper with a higher iteration count to get stored_email_key.
- The server then queries an internal only service to request the stored unique peppers for stored_email_key, and receives bcrypt_pepper and pbkdf2_pepper.
- password_key is hashed using SHA-512 to reduce the key length, so bcrypt doesn't truncate the input, resulting in reduced_hash.
- reduced_hash is then hashed using bcrypt, with bcrypt_pepper, resulting in bcrypt_hash.
- bcrypt_hash is then hashed using PBKDF2 with pbkdf2_pepper, resulting in the final, stored password hash.
email_key = PBKDF2(email, public_salt) password_key = PBKDF2(password, public_salt) POST: keys -> SSL -> server stored_email_key = PBKDF2(email_key, email_pepper) server gets bcrypt_pepper, and pbkdf2_pepper reduced_hash = SHA-512(password_key) bcrypt_hash = bcrypt(reduced_hash, bcrypt_pepper) pbkdf2_hash = PBKDF2(bcrypt_hash, pbkdf2_pepper)
Password - pbkdf2(bcrypt(sha512(pbkdf2(password, public_salt)), bcrypt_pepper), pbkdf2_pepper) Email address - pbkdf2(pbkdf2(email_address, public_salt), email_pepper)
Regarding email address storage
- Email addresses are only needed for notifications, password resets, and newsletters.
- Email addresses will be stored as a PBKDF2 hash on any system or cluster that handles any public traffic.
- Email addresses will be stored encrypted on a service accessible through an API available only to internal code that needs to send emails to recipients.
I realize this might be overkill, but I really want to ensure passwords as well as email addresses are properly handled and stored.
I'm thinking about generating 1024-bit keys where PBKDF2 is used, as well as using 2048-bit or larger salts.