-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider adding algorithm primers #48
Comments
I was redirected to this repo when reading https://github.com/RustCrypto/hashes, so I appreciate y'all saving me from the mistake of using Sha3 for creating password digests! |
You mean primers in the root README, correct? I don't think it will be reasonable to duplicate examples from crates documentation, but I guess a note which would recommend to use argon2 or scrypt could be a good addition. After introduction of a trait for password hashing functions we also could add examples similar to RustCrypto/hashes. |
That's exactly what I mean. The crates have great documentation on how to use the code, but assume knowledge of the algorithms in question. It would be helpful to provide some guidance about:
|
If you have any more questions/concerns I'll try respond to those too. Note I'm not a security professional or cryptographer, but I have done my own deep dive on the topic with plenty of notes. Hopefully I can save you and others some time/confusion. Attackers - Types of hardware they use for computationIn terms of security, an attacker will generally favor hardware that can greatly parallelize the computation to achieve a faster success rate. There's a few options for this, but they have their own tradeoffs in cost to performance and how the choice of a Password Hashing Function affects their viability.
How do we defend against the attacker?For a defender (you) the main concern is how much time it would typically take an attacker to on average guess the original password successfully. If the time is too long, the cost to perform it may be too high for an attacker to justify. Most users are not being directly targeted by such an attack, the attacker typically has access to a data breach of many user hashes and will prioritize their resources for low hanging fruit (weak passwords). The best the defender can do in this case is discourage the attacker by increasing the cost of computation. That's where choosing the right Password Hashing Function and parameters matters. It's unfortunately not as easy as advising everyone to use the same configuration as you need to balance security and usability (extending the time to compute affects response time as well as your own available resources under load).
Some are easier to attack, some are easy to configure but poor at balancing response time vs securitypbkdf2-hmac-sha256PBKDF2 is usually paired with HMAC-SHA256, but isn't limited to that. From what I understand, you set high amount of iterations (1Password and Bitwarden default to 100k iterations), and besides some additional overheads each iteration is roughly the cost of computing two SHA-256 hashes. Password managers tend to use this as a KDF for encryption keys derived from your master password. It's useful for client-side browser extensions which can use the native Web Crypto API browsers offer with support for PBKDF2, which your options lack and would thus incur higher performance overheads (maybe less of a concern today with WASM). On a GPU the memory requirements are too low AFAIK, that the attackers performance fairs much better than other options like bcrypt, requiring higher iteration counts (and thus computation time) or weaker security strength by comparison. It is NIST approved and valid for FIPS compliance I think, which the others may lack. Otherwise it's not likely a good choice for password storage vs your other choices. bcryptbcrypt only ever uses 4KiB of memory. This makes it an easier target to parallelize an attack against as the memory requirement is fixed and low.
bcrypt has been available since 1999, and is one of the most common choices you'll see in production. Dropbox, Auth0 and other popular services have blogged about how they utilize it. It's often the one you'll come across in frameworks or guides. It has a simple configuration, you have a Over time, as processing power becomes more affordable all you need to do is increment the WF and now an attacker needs 2x the resources. Your only defense however is increasing the computation time, if your own server hardware isn't also upgrading, this has a negative impact on you. scryptFor the most part, this aimed to address the weakness of bcrypt by increasing the memory requirements. This was notably beneficial as memory is relatively expensive, especially when you're adding it to ASICs or FPGAs and the defender can just increase memory requirements. That can reduce the amount of parallelization, or in some cases be too high requiring attacker hardware to be replaced which can be very costly. Unfortunately the memory isn't as flexible separately. You'll find the computation time is tied to the memory requirement. So the higher the memory required, the longer your CPU spends computing the hash result. You do want the computation time to be slowed down, but not too heavily that it negatively impacts your service. Parameter wise, you're still mostly looking at adjusting one parameter, it's very much like bcrypt. Instead of a work factor, you're increasing the amount of memory required, which will as mentions scales the computation time. There are two other parameters but generally they're not touched, the 2nd is related to memory access as AFAIK has little security value deviating from argon2Argon2 later arrived and is one you'll often see mentioned and advised to use today when possible. You can adjust iterations You can double/halve By raising the memory requirement sufficiently, the cost to the attacker is greatly increased by reducing their ability to parallelize computation (rate / number of password guesses). Unfortunately, despite some benefits over scrypt, performance in different environments seems to vary, this means depending on your server, if you compare the two by the time they both take to compute, argon2 may need to use less memory, I've fond it at parity on my system, but 4x slower on a VPS instance. There's also 3 modes,
Mostly bcrypt password input size (not length!)An attacker may try to stress a server by sending 1GB+ sized passwords, a good way around this is to use SHA-256 or similar to pre-hash the users input from the client-side, which has the additional benefit of your received user passwords always being the same size. pbkdf2You may come across this interesting tidbit, which allows a binary hash to match the same output as original string input that exceeds a certain length. An example of one of the collisions is given here:
That's not actually a problem for passwords to be concerned about. Still worth pointing out and clarifying :) bcrypt
scryptThe algorithm can be modified for TMTO attacks (Time Memory Trade Off). One is with parallelization of multiple cores that can reduce the time down by roughly 25% IIRC. The other allows trading additional CPU time for reducing required memory. This is roughly scaling the CPU time such that the time to compute the hash remains the same but resource requirements has become adjustable. As scrypt was designed to defeat attackers better by forcing cost to attack up with the cost of memory, this may allow an attacker to more affordable attack with hardware with limited memory which would otherwise have been unable to handle the memory requirements. argon2Two of the judges involved in the Password Hashing Competition have tweeted or mentioned elsewhere online that using argon2 for interactive login (where you want a hash computed in less than 1 second) is weaker than bcrypt against an attacker. This information was vaguely supported but many have circulated it. I contacted one of them and managed to get more information:
Thus despite their reputation, it's difficult to trust the validity of the advice in practice.
They're all better than nothing :)
AFAIK, bcrypt is the only one that isn't a KDF (separate use beyond a PHF). PBKDF2 and bcrypt aren't the latest but you'll find leading modern products with plenty of users and financing still favor and trust them. scrypt and notably argon2 can afford more security strength due to memory requirements, but do note for a web auth server, the memory usage required just for auth is going to scale based on concurrent users logging in.
Alternatives to PHFsFor web auth, there are alternatives (some involving no password storage or not requiring the user to use a password at all):
Short versionYou're probably going to be ok with bcrypt, OWASP still advises it, as does Mozilla.
|
@polarathene wow, awesome! Thanks for doing that! Would you mind opening a PR to add that to the toplevel README.md? There's probably some nits/small changes to be made, but those are easier to discuss as line notes in review. |
Seems a bit bulky for the README? Could be a separate document that the README gives a brief summary of and links to for more detail? I did run each locally with parameters to get a rough comparison of what equivalent runtime for each was, and then did so again on a cheap VPS instance where I found argon2 to become 4x slower than scrypt which it was otherwise on par with on my local system performance. I think that'd be useful to document in a table for others as a reference. I can put that information up as a PR in the meantime, but won't have much spare time to allocate until I resolve two other pressing tasks (might take a week or two). |
Sure, take your time |
Yeah, adding a separate file sounds like a good idea. Eventually we may move it into a "RustCrypto Book", assuming we will create it that is. |
The OWASP Password Storage Cheat Sheet seems pretty good: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html |
Yeah, I linked that earlier. Looks like it received some updates since. Perhaps a contribution for me isn't required? Still rather large backlog elsewhere 😅 |
Aah yeah, see it now. I like what you wrote, but also, I think the OWASP guide is pretty good, and if it's one less thing for us to maintain perhaps we should just link to that. |
Yeah, works for me :) If there's any need for more info on one of them from future issues, just refer to the comment I made 👍 |
I'm writing a server boilerplate that I hope to eventually use in production, right now it's just for fun. One of the things I'd like to do is store user accounts (and password digests!) in my database.
I noticed y'all have three algorithms listed but no guidance about how to choose one for the naive 🙋♂️ . Would it be reasonable to provide a primer about each algorithm and what settings it might be appropriate it?
The text was updated successfully, but these errors were encountered: