This question, but focused on .NET's given PBKDF2 implementation, has been asked here now.
Situation
I am currently updating a password storage mechanism for a website.
My plan for storing a password was
- Create a 16 byte salt*
- Create a 64 byte hash with that salt, using SHA512*
- Concatenate the 2
- Convert the whole thing to Base64
- Store the result in a 108 character DB field
The amount of iterations is still to be determined (aiming for an amount that will take a bit under one second per login).
*I am working in C#, using the language-provided RNGCryptoServiceProvider
and Rfc2898DeriveBytes
classes to create salt and hash respectively. This is a standard industry solution. Also it's a verified implementation, which apparently C# bcrypt is not
Do note Rfc2898DeriveBytes is Microsoft's implementation of PBKDF2. (Source Rfc2898DeriveBytes class)
Issue
Well, that was my plan until yesterday.
Then I read that it's a good idea to add the amount of iterations to the password, so that it can be updated to a higher iteration count later (when technology gets faster).
And then I found some suggestions for using a pepper, and some against it. Trying to figure that out ultimately led me to this highly upvoted answer on stackoverflow (last updated 2017)...
...which advocates using encryption instead of hashing, so that you can later re-encrypt all passwords with a higher iteration count/workload, instead of waiting for users to log in again (which might not happen at all anymore).
Common sense tells me that this is a good idea, but - let me put it this way -
Common sense does often not apply when it comes to security.
Question
How should I currently store a user's password in a database?
I would consider this one question, but there are several components to it:
- Salt length
- Pepper vs. no pepper
- Pepper length
- Encryption vs. hash
- Encryption/hash algorithm choice
- Encryption/hash length
- How to combine all that into one string for the DB
(For example, I have seen a pattern[salt]$[hash]$[iterations]
) - Reasonably future-proof length for the DB field
(Considering e.g. that iterations will get bigger over time)