2

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)
  • The accepted answer on that duplicate I linked to contains all the answers you need and is still 100% applicable. Definitely don't use SHA512! You want a KDF, not a hash function. Definitely don't encrypt. Use one of the suggested KDFs in that answer, most of which will automatically deal with the salt for you. They also have tuneable cost parameters so you can adjust iterations as desired. There is no need to do the hashing yourself - use a standard library – Conor Mancone Nov 05 '19 at 10:39
  • 1
    @ConorMancone "_You want a KDF, not a hash function [...] Use a standard library_" - you mean like the [Rfc2898DeriveBytes](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=netframework-4.8) that I mentioned I am using? – Raphael Schmitz Nov 05 '19 at 10:44
  • @ConorMancone I also still have to choose an algorithm for that, and SHA512 seemed like a good choice. The answer you linked is akin to a whole book, explaining a semester worth of theory, going into several different algorithms, filling whole screens with solutions _which they actually do not really advise_... all in all a bit unfocused when applied to a question which could be answered in 1 or 2 paragraphs. – Raphael Schmitz Nov 05 '19 at 11:03
  • @ConorMancone I hate you, I have noticed I was doing it wrong because of the linked answer – bradbury9 Nov 05 '19 at 11:36
  • @RaphaelSchmitz From here, your comment sounds like, "Why are you asking me to read the manual - just tell me what I want to know!". While I appreciate your desire to get to the bottom of things, your question showed a couple misunderstandings about a few different key aspects of password storage. As a result, I think reading the manual is really the right way to go here. Regardless, stackoverflow is not a fan of duplicates, and your question is definitely just a duplicate of the linked to question. Still, you might very well get an answer before this gets closed. – Conor Mancone Nov 05 '19 at 11:40
  • To further answer things though, SHA512 is not a good choice for passwords because it is a hash function, not a KDF, is intended to be fast, and is likely amenable to GPU acceleration. Instead use any of the three methods listed in that answer - all are fine choices. It mentions advantages and disadvantages, but that is not meant to imply that any of them are unsuitable. Rather, the answer is trying to give some additional details for people who really want to understand the differences. Otherwise, those are the only 3 reasonable choices right now. – Conor Mancone Nov 05 '19 at 11:42
  • Finally, I apologize for missing your mention of `Rfc2898DeriveBytes`. The reason for my confusion is because normally, with a library intended for password hashing, the details of salt generation and then output encoding are all handled by the library itself. Having taken a quick look at the library you are using, it does none of those things. All that to say: it looks like this is a relatively low-level library, and I suspect that you would be better off finding a 3rd party library in your language specifically intended for password generation. – Conor Mancone Nov 05 '19 at 11:44
  • As an example of something I'm talking about, see the standard function for password hashing in PHP: https://www.php.net/manual/en/function.password-hash.php All the things you are concerned about are automatically handled by the library. Unfortunately the library available to you is leaving far too much of the work for you to do, which is likely to lead to critical mistakes, as these things are surprisingly tricky. – Conor Mancone Nov 05 '19 at 11:45
  • This one is worth a read and may solve all your problems: https://github.com/defuse/password-hashing – Conor Mancone Nov 05 '19 at 11:54
  • @ConorMancone "_Unfortunately the library available to you is leaving far too much of the work for you to do, which is likely to lead to critical mistakes_" Well, that might be pretty much exactly why I'm asking. At this point, I can use unverified bcrypt or scrypt implementations (bad), an only 4 year old Argon2 (bad), as per LvB's answer here I can roll my own Balloon Hashing or Lyra2 (worse). Or I can use the official, verified PBKDF2 from Microsoft (seems acceptable to me) - but then I need the right input (the data I listed in the question, most of which the duplicate does not give). – Raphael Schmitz Nov 05 '19 at 12:10
  • Regarding "Pepper vs. no pepper", do note [this answer](https://security.stackexchange.com/a/31846/174855) extract "For peppering to be really applicable, you need to be in a special setup where there is something more than a PC with disks; you need a HSM" and with that I fully agree. – bradbury9 Nov 05 '19 at 12:10
  • @bradbury9 The argumentation I saw most was along the lines of an SQL injection giving an attacker readonly access to the database - in that case, a pepper _in the codebase_ is still unknown to them and supposedly helps. – Raphael Schmitz Nov 05 '19 at 12:12
  • Aww, now I can't even write an own answer with random values, which the internet will complain about until it's correct... guess I'll have to do that in a new question. – Raphael Schmitz Nov 05 '19 at 12:15
  • @RaphaelSchmitz a SQLi can lead to a code execution in the SQL server, and that to a full compromise, so code pepper is not enough imho. OP seems to be c# coder, so using parametrized queries should be pretty straighforward to avoid that readonly access. – bradbury9 Nov 05 '19 at 12:16

1 Answers1

3

Why do you want to create your own scheme for this? Please use one of the industry standard solutions for this:

I would suggest Bcrypt as that's the most accessible on a PC. but all of them have considerations you will probably miss if you try to implement your own (as would anyone).

This list is time sensitive, so please check for the current best practices. It is based in part on the list of key derivation functions from Wikipedia.

Anders
  • 64,406
  • 24
  • 178
  • 215
LvB
  • 8,217
  • 1
  • 26
  • 43
  • OK, same as the comment by Conor Mancone under the question: "_Please use one of the industry standard solutions for this_" - so, like [.NET's Rfc2898DeriveBytes](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=netframework-4.8) that I mentioned I am using? Concerning bcrypt, apparently that's [a bad choice](https://stackoverflow.com/a/4435888/5900626) in my situation. – Raphael Schmitz Nov 05 '19 at 10:48
  • as stated in that awnser use `PBKDF2` in C#. I am not a C# developer so I can not tell you if just the RFC2898 is enough – LvB Nov 05 '19 at 11:09
  • @LvB That class according to MS documentation is a PBKDF2 implementation. "Implements password-based key derivation functionality, PBKDF2, by using a pseudo-random number generator based on HMACSHA1" Source [.NET API reference](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?view=netframework-4.8) – bradbury9 Nov 05 '19 at 11:42
  • @bradbury9 Good to know. than it looks like Raphael Schmitz can just use that. – LvB Nov 05 '19 at 11:43
  • I have submitted an edit with the MS link, hope it makes it into the question. I think OP question is more likely "not sure if PBKDF2 is the best choice because of Pepper" – bradbury9 Nov 05 '19 at 11:48
  • Forget all, use Argon2 – kelalaka Nov 12 '19 at 20:02