Do any security experts recommend bcrypt for password storage?

  • On the surface bcrypt, an 11 year old security algorithm designed for hashing passwords by Niels Provos and David Mazieres, which is based on the initialization function used in the NIST approved blowfish algorithm seems almost too good to be true. It is not vulnerable to rainbow tables (since creating them is too expensive) and not even vulnerable to brute force attacks.

    However 11 years later, many are still using SHA2x with salt for storing password hashes and bcrypt is not widely adopted.

    • What is the NIST recommendation with regards to bcrypt (and password hashing in general)?
    • What do prominent security experts (such as Arjen Lenstra and so on) say about using bcrypt for password hashing?

    On this topic, this is an interesting read: http://www.tarsnap.com/scrypt/scrypt.pdf

    Yes, bcrypt has many savvy supporters, though of course you want to tune the number of iterations with performance and tune other defenses with DoS attacks in mind. See also How to securely hash passwords? - IT Security and Password Hashing add salt + pepper or is salt enough?

    @tkbx Some years ago, maybe. Actually, MD5 isn't considered secure anymore. Have a look at MD5 considered harmful today from 2008, for example.

    I meant any security expert would recommend bcrypt. `:P`

    As a quick note, I personally take either bcrypt or pbkdf2 (depending on the platform) and benchmark different settings. Depending on what I'm using it for I use settings that cause it to take a given time. Like for a desktop application I'd aim around 80ms (I have a relatively fast laptop), or for a server I'd benchmark on the server and aim depending on what traffic I'm expecting.

    To answer the question in the title: Yes. I am a Senior Information Security Architect and regularily write cryptographic policies for our customers. bcrypt is one of the algorithms I explicitly recommend for password hashing.

    Nowadays, in 2019, I would reach for Argon2 when hashing passwords.

  • Bcrypt has the best kind of repute that can be achieved for a cryptographic algorithm: it has been around for quite some time, used quite widely, "attracted attention", and yet remains unbroken to date.

    Why bcrypt is somewhat better than PBKDF2

    If you look at the situation in details, you can actually see some points where bcrypt is better than, say, PBKDF2. Bcrypt is a password hashing function which aims at being slow. To be precise, we want the password hashing function to be as slow as possible for the attacker while not being intolerably slow for the honest systems. Since "honest systems" tend to use off-the-shelf generic hardware (i.e. "a PC") which are also available to the attacker, the best that we can hope for is to make password hashing N times slower for both the attacker and for us. We then adjust N so as not to exceed our resources (foremost of which being the user's patience, which is really limited).

    What we want to avoid is that an attacker might use some non-PC hardware which would allow him to suffer less than us from the extra work implied by bcrypt or PBKDF2. In particular, an industrious attacker may want to use a GPU or a FPGA. SHA-256, for instance, can be very efficiently implemented on a GPU, since it uses only 32-bit logic and arithmetic operations that GPU are very good at. Hence, an attacker with 500$ worth of GPU will be able to "try" many more passwords per hour than what he could do with 500$ worth of PC (the ratio depends on the type of GPU, but a 10x or 20x ratio would be typical).

    Bcrypt happens to heavily rely on accesses to a table which is constantly altered throughout the algorithm execution. This is very fast on a PC, much less so on a GPU, where memory is shared and all cores compete for control of the internal memory bus. Thus, the boost that an attacker can get from using GPU is quite reduced, compared to what the attacker gets with PBKDF2 or similar designs.

    The designers of bcrypt were quite aware of the issue, which is why they designed bcrypt out of the block cipher Blowfish and not a SHA-* function. They note in their article the following:

    That means one should make any password function as efficient as possible for the setting in which it will operate. The designers of crypt failed to do this. They based crypt on DES, a particularly inefficient algorithm to implement in software because of many bit transpositions. They discounted hardware attacks, in part because crypt cannot be calculated with stock DES hardware. Unfortunately, Biham later discovered a software technique known as bitslicing that eliminates the cost of bit transpositions in computing many simultaneous DES encryptions. While bitslicing won't help anyone log in faster, it offers a staggering speedup to brute force password searches.

    which shows that the hardware and the way it can be used is important. Even with the same PC as the honest system, an attacker can use bitslicing to try several passwords in parallel and get a boost out of it, because the attacker has several passwords to try, while the honest system has only one at a time.

    Why bcrypt is not optimally secure

    The bcrypt authors were working in 1999. At that time, the threat was custom ASIC with very low gate counts. Times have changed; now, the sophisticated attacker will use big FPGA, and the newer models (e.g. the Virtex from Xilinx) have embedded RAM blocks, which allow them to implement Blowfish and bcrypt very efficiently. Bcrypt needs only 4 kB of fast RAM. While bcrypt does a decent job at making life difficult for a GPU-enhanced attacker, it does little against a FPGA-wielding attacker.

    This prompted Colin Percival to invent scrypt in 2009; this is a bcrypt-like function which requires much more RAM. This is still a new design (only two years) and nowhere nearly as widespread as bcrypt; I deem it too new to be recommended on a general basis. But its career should be followed.

    (Edit: scrypt turned out to not to fully live up to its promises. Basically, it is good for what it was designed to do, i.e. protect the encryption key for the main hard disk of a computer: this is a usage context where the hashing can use hundreds of megabytes of RAM and several seconds worth of CPU. For a busy server that authenticates incoming requests, the CPU budget is much lower, because the server needs to be able to serve several concurrent requests at once, and not slow down to a crawl under occasional peak loads; but when scrypt uses less CPU, it also uses less RAM, this is part of how the function is internally defined. When the hash computation must complete within a few milliseconds of work, the used RAM amount is so low that scrypt becomes, technically, weaker than bcrypt.)

    What NIST recommends

    NIST has issued Special Publication SP 800-132 on the subject of storing hashed passwords. Basically they recommend PBKDF2. This does not mean that they deem bcrypt insecure; they say nothing at all about bcrypt. It just means that NIST deems PBKDF2 "secure enough" (and it certainly is much better than a simple hash !). Also, NIST is an administrative organization, so they are bound to just love anything which builds on already "Approved" algorithms like SHA-256. On the other hand, bcrypt comes from Blowfish which has never received any kind of NIST blessing (or curse).

    While I recommend bcrypt, I still follow NIST in that if you implement PBKDF2 and use it properly (with a "high" iteration count), then it is quite probable that password storage is no longer the worst of your security issues.

    What is a "high" iteration count? And I don't really understand why you still recommend bcrypt over PBKDF2

    TL;DR: bcrypt is better than PBKDF2 because PBKDF2 can be better accelerated with GPUs. As such, PBKDF2 is easier to brute force offline with consumer hardware.

    @ExplosionPills My rule of thumb for a "high" iteration count is: as large as possible without causing too many users to complain about the delay. I generally tune bcrypt / pbkdf2 to take around 250-500ms (1-2s for admin accounts) on my production server while it's under light load. This policy seems to track w/ OpenBSD's recommended bcrypt settings, and any other context-dependant recommendations I've found.

    No word about scrypt? :)

    @EliCollins: I'm rather curious here - could using a high iteration count introduce the possibility of DDOS attacks against the server? For example, if I were to POST hundreds of login attempts to the server simultaneously, the server would immediately become overwhelmed with attempting to hash all of the passwords. Are there ways to avoid that potential problem?

    @GeorgeEdison I've been wondering about that question too, and haven't found a good answer. 1) You could rate-limit the login page based on load, but that doesn't scale well. 2) offload hashing to a separate server, so the DDOS just affects concurrent logins, but that's just a variant of option 1. 3) use a nonce (e.g. the csrf token) to add a hashcash-style proof of work to your login, but that doesn't help much against a botnet. 4) The DDOS first has to find a valid user account, so delay invalid-user errors with a sleep() call to match the hash time.

    @GeorgeEdison to avoid DDOS check the username before checking the password. Then if there have been multiple failed login attempts in a row for that user (say, 10 of them) reject the password without even checking it unless the account has "cooled off" for a few minutes. This way an account can be DDOS'd but not the whole server, and it makes even horribly weak passwords impossible to brute force without an offline attack.

    @AbhiBeckert What happens if the legitimate user gets caught trying to log in while his account is under attack from a botnet? (Answer: DOS). Also, skipping the password check for bad usernames provides a timing-based oracle that tells the attacker which accounts exist. If you retain the IP for successful logins (there are good reasons not to), you can give those IPs priority and check the password even if the username is "locked out".

    NIST's blessing doesn't carry the same weight it used to. http://www.wired.com/threatlevel/2013/09/nsa-backdoor/all/ (I doubt this applies to PBKDF2, but it's food for thought.)

    @TerisRiel I don't mind if a user's account goes offline due to a DOS attack. In the real world it doesn't actually happen, and if it does... well that's better than the account being hacked.

    Sure, DOS the user, they'll get some warning that will alert them that something is going on (if, as @Abhi Beckert says, it ever even happens in real life). Avoid an oracle attack by sleeping any failed usernames for the amount of time you'd take to check the password.

    is scrypt still too new to be used?

    @JanusTroelsen: scrypt turned out not to be as good as expected; I added an appropriate paragraph in the answer.

    @MatthewMartin: you read the article wrong -- bcrypt is not cracked at all; the attacked passwords were hashed twice, once with bcrypt and once with a homemade, weak invocation of MD5. The researchers broke the weak homemade hash based on MD5 (that is, they did not "break" it, they just tried a lot of passwords until a match was found, and it was fast because the homemade hash was very fast and unsalted).

    @ThomasPornin So basically they decided to make bcrypt "even more secure"? My rule of thumb is "If this improvement is so good, the experts would have found it long before me."

    @corsiKa: it was more a case of two developers working over the same user passwords, but not talking to each other; one knowing what bcrypt is, the other not knowing. This is a case of a generic rule: if the attacker has the choice between two ways to obtain the information he seeks, he will choose the easiest for him.

    How up-to-date is this answer in 2016? For example, how does Argon2 (which won the Password Hashing Competition) hold up compared to PBKDF2, bcrypt, etc? As this is a widely-read post it would be good to see periodic updates to this answer that either reaffirm its relevance or update its recommendations.

    @ThomasPornin I feel like a paragraph on Argon2 would be the cherry-on-top of this already excellent answer.

    @joshreesjones I think that merits a separate question, as this question and answer are specifically about bcrypt.

    Are you turing complete?

    To the suggestion one should check the username first, then fast-fail if the username isn't present -- that means an attacker can learn which usernames exist and which do not. To keep from leaking that, you'd want every password check to be the same length of time.

    @MikkoRantalainen Modern GPUs (even in 2012 when you wrote that comment) can easily handle bcrypt. The requirement is for 4 KiB of fast memory. This means that each GPU shader needs to have at least that much memory allocated to it from the global pool of memory. A GPU with, say, 500 shaders and 1 GiB of VRAM will be able to give each and ever shader a full **2 MiB** of memory. Even one with 256 MiB of VRAM (which is very low) can allocate over a hundred times more memory than bcrypt requires to 500 shaders. Now, ASICs on the other hand...

    @forest the point of bcrypt and GPUs is not that bcrypt requires massive amounts of memory. If you need to spend lots of memory, see scrypt. The bcrypt algorithm is about random access of RAM which was not the strong point of GPUs. Newer GPUs have less problems with that but PBKDF2 is still easier task for GPUs.

    @MikkoRantalainen Is the random access heavily key-dependent? Random access on a single 4 KiB page of memory seems like it wouldn't be particularly difficult for a GPU, even if GDDR5 memory has higher latency than DDR3 or DDR4.

    @forest I'm not sure if access pattern depends on key. According to https://github.com/freedomofpress/securedrop/issues/180#issuecomment-29662610 https://www.usenix.org/system/files/conference/woot14/woot14-malvoni.pdf https://crypto.stackexchange.com/a/401/1267 and https://crypto.stackexchange.com/a/35352/1267 it seems GPUs have problems with pseudorandom access patterns that *modify* the memory and as a result, GPUs are not faster with bcrypt than similarly priced CPUs.

    @MikkoRantalainen Oh that's interesting! So it seems each group of cores has its own shared memory (which is very fast), along with the main memory (which is slow for concurrent access). At least in 2011, the shared memory was too small to hold 4 KiB. I wonder if that has changed for any modern GPUs.

    See also: "Balloon Hashing: A Memory-Hard Function Providing Provable Protection Against Sequential Attacks" https://eprint.iacr.org/2016/027.pdf (especially chapter 5)

    I am a noob. Someone tell me why I shouldn't just sha256 my password first and then run it through bcrypt or scrypt. No attacker is going to check for both if most of the internet ain't doing it.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM