Is Bcrypt a Good Hashing Algorithm to Use in C#? How to Find It

Is BCrypt a good hashing algorithm to use in C#? Where can I find it?

First, some terms that are important:

Hashing - The act of taking a string and producing a sequence of characters that cannot be reverted to the original string.

Symmetric Encryption - (Usually just referred to as 'encryption') - The act of taking a string and producing a sequence of characters that can be decrypted to the original string through the use of the same encryption key that encrypted it.

Rainbow Table - a lookup table that contains all variations of characters hashed in a specific hashing algorithm.

Salt - a known random string appended to the original string before it is hashed.

For the .NET Framework, Bcrypt does not yet have a verified reference implementation. This is important because there's no way to know if there are serious flaws in an existing implementation. You can get an implementation of BCrypt for .NET here. I don't know enough about cryptography to say whether it's a good or bad implementation. Cryptography is a very deep field. Do not attempt to build your own encryption algorithm. Seriously.

If you are going to implement your own password security (sigh), then you need to do several things:

  1. Use a relatively secure hash algorithm.
  2. Salt each password before it's hashed.
  3. Use a unique and long salt for each password, and store the salt with the password.
  4. Require strong passwords.

Unfortunately, even if you do all this, a determined hacker still could potentially figure out the passwords, it would just take him a really long time. That's your chief enemy: Time.

The bcrypt algorithm works because it takes five orders of magnitude longer to hash a password than MD5; (and still much longer than AES or SHA-512). It forces the hacker to spend a lot more time to create a rainbow table to lookup your passwords, making it far less likely that your passwords will be in jeopardy of being hacked.

If you're salting and hashing your passwords, and each salt is different, then a potential hacker would have to create a rainbow table for each variation of salt, just to have a rainbow table for one salted+hashed password. That means if you have 1 million users, a hacker has to generate 1 million rainbow tables. If you're using the same salt for every user, then the hacker only has to generate 1 rainbow table to successfully hack your system.

If you're not salting your passwords, then all an attacker has to do is to pull up an existing Rainbow table for every implementation out there (AES, SHA-512, MD5) and just see if one matches the hash. This has already been done, an attacker does not need to calculate these Rainbow tables themselves.

Even with all this, you've got to be using good security practices. If they can successfully use another attack vector (XSS, SQL Injection, CSRF, et. al.) on your site, good password security doesn't matter. That sounds like a controversial statement, but think about it: If I can get all your user information through a SQL injection attack, or I can get your users to give me their cookies through XSS, then it doesn't matter how good your password security is.

Other resources:

  1. Jeff Atwood: .NET Encryption Simplified (great for an overview of hashing)
  2. Jeff Atwood: I just logged in as you
  3. Jeff Atwood: You're probably storing passwords incorrectly
  4. Jeff Atwood: Speed Hashing

Note: Please recommend other good resources. I've must have read a dozen articles by dozens of authors, but few write as plainly on the subject as Jeff does. Please edit in articles as you find them.

Hash Password in C#? Bcrypt/PBKDF2

PBKDF2

You were really close actually. The link you have given shows you how you can call the Rfc2898DeriveBytes function to get PBKDF2 hash results. However, you were thrown off by the fact that the example was using the derived key for encryption purposes (the original motivation for PBKDF1 and 2 was to create "key" derivation functions suitable for using as encryption keys). Of course, we don't want to use the output for encryption but as a hash on its own.

You can try the SimpleCrypto.Net library written for exactly this purpose if you want PBKDF2. If you look at the implementation, you can see that it is actually just a thin wrapper around (you guessed it) Rfc2898DeriveBytes.

BCrypt

You can try the C# implementation named (what else) BCrypt.NET if you want to experiment with this variant.

Disclaimer: I have not used or tested any of the libraries that I have linked to... YMMV

Which of the .NET included hashing algorithms are suitable for password hashing?

I think it's not really a meaningful Class name, but I do think it is included in the .NET framework. According to multiple sources, Rfc2898DeriveBytes is actually a PBKDF2 implementation. MSDN says so as well.

See
Why do I need to use the Rfc2898DeriveBytes class (in .NET) instead of directly using the password as a key or IV?
and
PBKDF2 implementation in C# with Rfc2898DeriveBytes

for example.

which algorithm preferred for hashing passwords C#?

MD5:

In 1996, a flaw was found with the design of MD5, and while it was
not a clearly fatal weakness, cryptographers began recommending the
use of other algorithms, such as SHA-1—which has since been found to
be vulnerable as well.

SHA1:

In 2005, cryptanalysts found attacks on SHA-1 suggesting that the
algorithm might not be secure enough for ongoing use

SHA2 which SHA256 is a type of does not have a known vulnerability as of the moment of writing.

Best practice of Hashing passwords

Sure SHA1 is more secure that MD5, but for most purposes it is not secure enough.

You will probably find useful the video How NOT to Store Passwords by Computerphile - 9 minutes and 24 seconds long.

You must realize that there is much to cover when it comes to authentication and access control, so having a good hashing scheme is not enough.


On storing passwords.

As you already know, you don't store the password. In fact, when it comes to storing passwords, in general terms you want to store salted hashes of the passwords, using a modern algorithm optimized for this purpose. For the salt it is considered ok to store it alongside to the hash, for the salt value use a random value as long as possible.

Note: When generating random value for security purposes, use a cryptographic secure generator (such as a subclass of RandomNumberGenerator for .NET - example). This random number generator are designed to be hard to predict. While standard random number generator are meant to be repeatable (That is with System.Random all you need is the seed to generate all the values, and to guess the seed all you need is enough consecutive values generated with the same seed).

Also note: Most hashes are optimized to be fast to calculate, in that category falls both MD5 and SHA1. You should choose one that is not that fast, so that the attack will take a reasonable amount of time to compute the hashes when trying to crack your passwords.

One such algorithm is BCrypt - others include Scrypt and PBKDF2 - on using BCrypt from C# you will find the article Use BCrypt to Hash Your Passwords: Example for C# and SQL Server useful. If you can't resource to BCrypt or similar algorithm, you should atleast use a variant of SHA2 (SHA256, SHA512 and so on).

Addendum: You can use the class HMACSHA256 which is available in the BLC as a key derivation function, pass your salt as key. This is preferible to appending or prepending the salt (which could fall to Length extension attacks). That is, if you use HMAC, and your hash algorithm is vulerable to Length extension attacks (known or to be discovered), your system is till secure. MD5, SHA1 and SHA2 as suceptible to this kind of attack. SHA3 is not. Sadly SHA3 is not included in the BLC (no, it is not SHA384), you can get it from Multiformats.Hash or HashLib. I have to mention that SHA3 is also designed to be fast when implemented in hardware. And remember, for passwords an slow hash is better.


Addendum: Argon2

As it was pointed a year ago this answer should be updated to mention of Argon2. I did write the original answer before that existed.

At the time, I had not found an implementation for C# that I was willing to recommend. Since this answer was brought to my attention, I had another look, and that is no longer the case.

You can use Isopoh.Cryptography.Argon2 which has fully managed code (it is not a C# binding for a C++ implementation, but full C# code), works on all major platforms and there are Nugets available.

Notes:

  • Use Argon2Version.Nineteen. This is Argon2 v.1.3 (Nineteen = 0x13) which fixes known vulnerabilities.
  • Use Argon2Type.DataDependentAddressin (Argon2d), or use Argon2Type.DataIndependentAddressing (Argon2i) with TimeCost >= 10. Argon2d is in theory vulnerable to side channel attacks, as such it is not recommended for code that runs on client machines. Isopoh.Cryptography.Argon2 mitigates this by using OS calls to prevent sensitive memory to be moved to virtual memory/pagefile/swap and zero it as soon as possible. On the other hand Argon2i has a Time-memory tradeoff vulnerability, which allows to compute the hashes faster by using more memory. The paper Towards Practical Attacks on Argon2i and Balloon Hashing shows that you need 10 iterations/passes to make the exploit inefficient, even in Argon2 v.1.3.

Here are some recommended reading:

  • Speed Hashing
  • You're Probably Storing Passwords Incorrectly
  • Everything you ever wanted to know about building a secure password reset feature
  • The definitive guide to form based website authentication
  • OWASP's Password Storage Cheat Sheet
  • OWASP's Forgot Password Cheat Sheet

Also the video: Crypto is Back! - Google Tech Talk - August 5, 2009 - 54 minutes and 32 seconds long.


On recovering a password.

First off: don't. The point of the password recovery option is not to recover the password, but to recover access to the application. So... how do you recover access to the application?

I'm glad you ask. What you need is an alternative way to verify the identity of the user. This could be a second factor authentication (anything from security question to using a hardware key generator). Yet, what is often done is to resource on third party, such as mail.

So, you want to know if the user is the owner of the email (or cellphone, or whatever) the user has previouly claim to own. In order to do so you send a code (often refered as token or cookie) to that email (or whatever). This must be a random generated code with a cryptographic secure generator so that nobody else - except the owner of that email (or whatever) - will be able to know what that code is.

Now, if the user presents to your application that code, you are almost sure it is the right user.

Almost because: emails (or whatever) could has been stored in an unsecure location. To mitigate that, you want to put a time limit on your code (cookie or token). Also, if a code has been used, it should NOT work again. And for extra security you could resource to a CAPTCHA, to ensure this code doesn't come from a bot that just got lucky.

For more on this topic (this links are also presented above):

  • Everything you ever wanted to know about building a secure password reset feature
  • OWASP's Forgot Password Cheat Sheet

Hashing a password with pepper and salt in C# using Rfc2898DeriveBytes

PBKDF2 (with SHA-1) - which the terribly named Rfc2898DeriveBytes implements - uses a repeated HMAC with the password - encoded to bytes - as key. Generally, HMAC simply performs a padding method on the input key (ipad and opad if you want to look it up). This padding goes up to the input block size of the hash function. However, let's look at the definition of HMAC in the HMAC RFC:

We denote by B the byte-length of such
blocks (B=64 for all the above mentioned examples of hash functions),
and by L the byte-length of hash outputs (L=16 for MD5, L=20 for
SHA-1). The authentication key K can be of any length up to B, the
block length of the hash function. Applications that use keys longer
than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC.

In your case, if your encoded password is longer than 64 - 16 = 48 bytes (with 16 the pepper size) your HMAC may be slower. A smart PBKDF2 function however could detect this and work around the issue by performing the initial hashing part only once.

So if your password is over 48 byte then you could give some benefit to the attacker if:

  1. your implementation isn't that smart and
  2. the implementation of the attacker is smart.

Note that the hash output size has nothing to do with this. You could use PBKDF2 with SHA-512 - with a block size of 1024 bits rather than 512 bits for SHA-1 and SHA-256 - in case this is a problem.

The hash output size of the hash function within PBKDF2 (SHA-1 by default) does matter if you request more than the output size of bytes. In that case you are also handing back advantage to an attacker. Fortunately you're only asking for 16 bytes in hashSize (a variable name that you might want to change to passwordHashSize to avoid confusion).


I understand that:

Oh dear ;)

  • A hash isn't reversible.

A cryptographic hash function and password hash function isn't reversible, other hash functions may be reversible.

  • A salt and pepper are added to increase security and prevent rainbow table attacks.

You only require a salt for that. A pepper prevents an attacker from guessing the password altogether, if it can be kept safe and if it is strong enough.

  • A salt is a unique and random string, it doesn't have to be secret and can be stored alongside the hash in a database.

That's correct.

  • A pepper is not unique and it is used for every hash. It is a secret and it isn't stored in the database.

Or it is encrypted itself and stored in the database, but yeah, in the end it needs to be secured one way or the other.

  • At least a 128-bit (16 bytes > 16 characters) should be used for the salt and pepper.

Well, it's kind-of the upper limit, I'd say 64 to 128 fully random bits, preferably over 80. However, not every character can be mapped to a byte, so your pepper is unlikely to be fully random - a bad idea for something that is basically a secret key.

Note that the salt configuration option of PBKDF2 may be any size up to 64 - 8 - 4 = 52 bytes before another block encrypt is needed for SHA-1. For that reason the salt & pepper are usually concatenated. This will also allow you use a true random pepper. It leaves more characters (64) for the password as well that way.

  • At least 10.000 iterations should be used for the algorithm.

Commonly we recommend about a million nowadays. But really, any amount of CPU cycles you can spare makes it harder to an adversary. The point is bit moot if the adversary really cannot get to the pepper of course. In that case a single round is enough - but you may want to use a higher iteration count as a second line of defense (e.g. against sysadmins trying to get to passwords of your users, using a copy of the database).

Keep in my DB the hashed passwords, not the original passwords, right ? so how do I use BCrypt.Net.BCrypt?

You are 100% correct when you state:

I thought I should keep in my DB only the hashes, not the passwords themselves.

Following on from our online chat, where we clarified the issue you were querying, the general process is as follows:

  • During the process of creating (or altering) a password, the password - in plain text - comes into the system in plain text.
  • It is then hashed in-memory.
  • This hash value is then saved in the database.

Later on ...

  • When a user wishes to be authenticated, they enter their password.
  • This password comes into the system in plain text.
  • It is then hashed in-memory.
  • This hash value is then compared with the previous hash saved in the password.

The important thing to note is that, for a given hash algorithm, two identical strings will always hash to identical values, so this comparison is safe.

It is standard to allow the password to be in plain text when still in memory. It is in theory possible to encrypt it before it reaches the server (for example, it would not surprise me if there exist SHA-512 procedures written in JavaScript to hash passwords before they are submitted), but that generally is above and beyond even the most complex security requirements.



Related Topics



Leave a reply



Submit