I've read about the concepts presented in these two questions:

Pre-hash password before applying bcrypt to avoid restricting password length

Would it make sense to use Bcrypt and PBKDF2 together?

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.


  1. Is this implementation relatively secure compared to most others?
  2. Have I shot myself in the foot by chaining multiple functions in this fashion?
  3. Is there a better way to take advantage of current 'approved', 'proven', and memory-expensive algorithms?
  4. How can I improve this implementation?
  5. 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?


  1. Man in the middle attacks
    • SSLStrip
    • ARP spoofing, DNS hijacking, and acting as HTTP proxy
      • Terminating SSL connection on attacker’s side and sending HTTP traffic to user
  2. Plain text password being handled by server code
  3. Rainbow tables and birthday problem
  4. Unproven algorithms
  5. Quick and efficient computation of hashes
  6. Really long credentials
  7. Lack of suitable entropy
  8. Authentication database dump results in disclosure of salts and peppers
  9. Plain text storage of email


  1. Never send plaintext password over the wire, and utilize SSL only across site
  2. Salt and hash the password before sending it to the server
  3. User unique peppers are applied on the server
  4. Hashing chain ends with PBKDF2, an algorithm vetted by RSA
  5. Hashing chain utilizes bcrypt, as it requires 4kB of memory
  6. Any input string to bcrypt is hashed with SHA-512 to keep the number of input characters under the maximum
  7. Salt and peppers will be long, random strings
  8. Store private salts and peppers on another service only accessible from internal API
  9. Store hashes of email addresses as well as passwords on servers that handle public traffic



SHA-512, bcrypt, PBKDF2

Public salts


Private pepper

email_pepper, bcrypt_pepper, pbkdf2_pepper

User global

public_salt, email_pepper

User unique

bcrypt_pepper, pbkdf2_pepper


  1. User enters email address and password in a web form, and submits them to log in.
  2. 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.
  3. Server hashes email_key again with PBKDF2 using email_pepper with a higher iteration count to get stored_email_key.
  4. 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.
  5. password_key is hashed using SHA-512 to reduce the key length, so bcrypt doesn't truncate the input, resulting in reduced_hash.
  6. reduced_hash is then hashed using bcrypt, with bcrypt_pepper, resulting in bcrypt_hash.
  7. 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

  1. Email addresses are only needed for notifications, password resets, and newsletters.
  2. Email addresses will be stored as a PBKDF2 hash on any system or cluster that handles any public traffic.
  3. 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 still debating on what values to use for the number of iterations on each step of the process. Obviously, I'm going to be using a larger number of iterations on the server than in javascript.

I'm thinking about generating 1024-bit keys where PBKDF2 is used, as well as using 2048-bit or larger salts.

  • 1
    Welcome to Security.SE! Reading through this I'm having trouble figuring out what your actual question is. Would you care to clarify it, please?
    – Scott Pack
    Nov 2, 2012 at 3:36
  • read: security.stackexchange.com/questions/21263/…
    – Jacco
    Nov 2, 2012 at 6:54
  • 2
    As @Jacco, and no doubt others will tell you in great detail - do not roll your own crypto! - See meta.security.stackexchange.com/a/915/485
    – Rory Alsop
    Nov 2, 2012 at 7:36
  • I already know that rolling your own crypto is a bad idea. That's why I'm using standard cryptographic primitives, and not coding up my own. @Jacco - Thanks for the tip on combining salt and pepper for bcrypt. Nov 2, 2012 at 20:26
  • @ScottPack - I'm actually asking five questions in one regarding this authentication and password/email storage implementation I've come up with. The questions are listed at the top, and the implementation follows down to the bottom. Nov 2, 2012 at 20:36

1 Answer 1


The first rule of security is: Don't roll your own crypto.

You've come up with a complex schema, that does not really improve anything over the best practices. I'm also not sure you're clear on what a salt nor pepper is, and why they is used: a salt is a random set of bytes of fixed length and are not considered a secret. A pepper is a (secret) key.

Client side hashing
Hashing values on the client side does not gain you anything, because:

  1. You're already using SSL, so everything is encrypted before being send over the wire
  2. If you hash, for example a password, on the client side, the hash of the password effectively becomes the password.

Email adresses
You usually want the users email address in reversible/readable form, because you need it at some later point to communicate with the user. If you have only a derived value, you can't contact them.

Pre hashed passwords
BCrypt truncates any password to 55 bytes of input (excluding the salt). 55 bytes may not seem like a lot in the light of commonly quoted values of 128 bits, 256 bits or even 1024 bits. However, common passwords are reality of lengths in the range of 6 to 10 characters.

If you wanted to allow for longer values, I would simply use


The need for a pepper value is debated here. If you decide you do want to store your passwords using both pepper & salt, use:

bcrypt(hash_hmac('sha256', $password, $pepper));*

As recommended here.

*) In both examples, I presume that you use a BCrypt implementation that automatically creates a salt

  • I am most definitely not implementing my own cryptographic primitives. The problem I see with not hashing client side is the plain text password gets sent to the server. If the server gets compromised, an attacker can just sniff all of the incoming traffic. What I'm trying to do is protect the plain text passwords, as most users reuse their passwords across services. About the email addresses, you'll see that I addressed the issue about being able to send emails out by separating the encrypted (reversible) storage from the authentication storage. Nov 2, 2012 at 20:32
  • Also, because the bcrypt output is going to be hashed by PBKDF2, I need to store the 'salt' somewhere. Storing it somewhere else other than where the hashes are stored (on another server) technically makes it a secret. For both the public salt and pepper, I am using a long string of characters. Nov 2, 2012 at 20:41
  • @clayrichardson unless you know how to do it properly, combining two good crypto primitives often produces a crypto that is less secure than the original cryptos they were made of, that's why people always say never to create your own crypto.
    – Lie Ryan
    Nov 3, 2012 at 10:48
  • 1
    @clayrichardson, Then, the assumptions you make are flawed. If an attacker gains the position to sniff the incoming traffic on you machine, you have already lost. Any value the machine can access, be it from a remote service or local value, will be readable to the attacker. Storing keys on a remote server increases complexity and opens up more attack options, but does not increase security. Because, you need to assume an attacker will be able to read any value, be it remotlely stored keys or email addresses.
    – Jacco
    Nov 5, 2012 at 7:40
  • 1
    @clayrichardson, then, the idea of hashing password on the client side has been debated over and over again. It will not add anything over that TLS does. If you server gets compromised to the point where the attacker can sniff incoming traffic on the server side, you have lost the battle. The attacker at that point is in control of your server and could just modify the html and/or javascript as he or she pleases. A client side script will not protect against a compromised server.
    – Jacco
    Nov 5, 2012 at 8:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .