Django-bcrypt
github.com
github.com
Step 2: Add to INSTALLED_APPS
Step 3: Enjoy more secure password hashing.
...
Step 4: Set BCRYPT_ROUNDS to something higher than 12 when computers get faster.
It took about 30-40 minutes to:
* Add the BCRYPT_ROUNDS setting.
* Remember how to allow for an optional setting (`if foo in settings` and `settings.get(foo, default)` don't work).
* Add the setup.py file.
* Write a README.
* Test with a real Django site to make sure nothing was broken.
* Create and push to repos on BitBucket and GitHub.
Instead of:
settings.get(foo, default)
Try: getattr(settings, foo, default)
This looks useful. Thanks.SHA is designed to be fast, so dictionary attacks can find huge amounts of matches nigh-instantly, and rainbow tables (or GPU EC2 instances!) exist to brute-force up to 8-12 characters, more than enough to crack most people's passwords. You can salt the hash (basically: pre-pend a unique string to the password) to defeat rainbow tables, but SHA doesn't do this by default. You can SHA something more than once to slow things down, but SHA doesn't do this by default (and rainbow tables still work - they're based on repeated hashings).
BCrypt is slow by design (ie, thousands of times slower). It's a much-harder calculation, and it automatically uniquely salts what it's hashing, and you can make it more secure incrementally by simply running it through more steps (it's designed to do this). The speed isn't noticeable to a user while logging in because checking if their password is correct is still extremely quick, but attempted-brute-forcers run up against a brick wall as their attempts are now rainbow-table-proof and slower by orders of magnitude. Best of all: it does all this by default, and it stores all the necessary information in the result so comparing values against it is fool-proof. You can't mis-use it.
SCrypt takes all the advantages of BCrypt and goes a step further: it guarantees that a certain amount of memory (ie, large) is required to perform the hashing function. So while SHA / BCrypt can still be attacked more quickly by, say, performing a thousand operations at a time through custom hardware, SCrypt can demand so much memory that it's simply infeasible to do so, so you're stuck testing one. password. at. a. time. It's the ultimate death to brute-forcing, basically.
For an example of complete and wanton irresponsibility, see Plenty of Fish.
First, they had access to the complete Gawker database, could have downloaded it and run their attacks on their side. They still would have needed to use blowfish/bcrypt (afaik), but your high rounds are useless if an attacker gets the actual data out of your infrastructure, just like they are with normaler hashes.
Second, the Gawker guys had no salting, and they used DES, which, as you know, was deprecated before many HN users were born. This made cracking the passwords really easy ... not that it would be that much harder with a conventional salting mechanism or even with bcrypt.
I was not contending that bcrypt is not like really great, just that it's not "completely irresponsible" to not use bcrypt or scrypt. bcrypt is still vulnerable to dictionary attacks, the only difference is the amount of time it will take to get everyone's password out, which, again, is useless in a case like gawker where the dumps are distributed all over the internet for anyone with an interest to peruse.
Please correct me if I'm wrong.
You're right that I don't have a very deep cryptographic background, but from everything I've seen and read, dictionary attacks still work on bcrypt'd passwords, just more slowly than otherwise. This is the part I want to know if I'm wrong about; is there some a reason a dictionary attack doesn't work on bcrypt? I understand that it will take longer to get 100% password recovery out of a bcrypt'd database than a database hashed with SHA-256 or SHA-512. Let's say that's not relevant because our theoretical attacker has A) a computer from the future which is fast enough to blow through bcrypt just as quickly as SHA-* or MD5; or B) is only interested in one user's password, so the expense is bearable. In these situations, does bcrypt provide extra protection that isn't routinely implemented in a standard hash?
My understanding is that bcrypt does not provide much besides the slowdown. I'm not trivializing the slowdown, I understand completely why it is beneficial to keep passwords away from bad guys as long as possible after a data leak. I am merely saying that if I'm correct and the only thing standing between bcrypt and SHA-* for a captured password database is X iterations of silicon, I don't really see using SHA-* hashes as "completely irresponsible".
I would have ignored your original comment and avoided pedantry except for your original assertion that your salting did something to mitigate the risk of not using bcrypt. It does no such thing. There's nothing else for us to argue about.
I'll take patches/pull-requests from either.
Be careful.
def encode_password(password):
"""Return crypt() encoded password"""
# Uses glibc SHA-crypt extension via $6$
salt = b64encode(os.urandom(8)).rstrip('=')
return crypt(password, '$6$' + salt)And I didn't test any SHA-x algorithms. Any algorithm that has a time you can't easily increase by tweaking a number (BCRYPT_ROUNDS) will eventually become insecure as computers get faster.
Like md5crypt, this new crypt() is not based on established cryptographic principles.
import hashlib
def slow_hash(password, salt, iterations=2000):
h = hashlib.sha1()
h.update(password)
h.update(salt)
for x in range(iterations):
h.update(h.digest())
return h.digest()
I haven't tested this code, but hopefully it illustrates the general idea. Repeatedly run the hash function on the output of the previous iteration. You may need to bump up the number of iterations later as computers get faster. It's not as good as bcrypt, but it also doesn't suck, and it should run just fine on App Engine.What is the cost (time) of using bcrypt instead of the default?
First, There's no such things as "the django overlords". We're an open source community of thousands. Hundreds contribute. Dozens have commit access.
Vry few among these contributors, and none (that I know of) on the commit team thinks that bcrypt support "is not important." If you'd like to prove me wrong, please cite your source.
Now, this has been proposed a few times (http://code.djangoproject.com/ticket/5787, http://code.djangoproject.com/ticket/5600). Each time, substantive problems with the approach have been found. A few choice quotes from the discussion should illustrate the issues:
Me, on #5600: "[T]here's a problem with supporting any hash schemes not in Python 2.3 (our lowest supported Python version): it means databases created with a different version of Python break when used under a lower one." (Now it's Python 2.4, and 2.5 soon, but the problem still holds.)
Malcolm, on #5787: "As soon as you start generating passwords that are only computable based on an optional model, the database can only be used with Django installations that have that model available. This removes the ability to move the database around easily. Django operates on a "batteries included" philosophy for exactly that reason: runs anywhere without lots of extra dependencies."
Worse, this third-party module (i.e. Python bcrypt module), last I checked, failed to build on Windows. As much as I hate supporting Windows, I recognize why we have to.
What I'm trying to say is that the issue is technical, not personal or political as you seem to think. If the technical issues can be overcome, I don't see why bcrypt support couldn't be the default.
Finally, I'll close with the obligatory note that open source projects get driven forward by people scratching their own itch. If this bothers you, fix it. If your fix is rejected, try again. If you think we're a bunch of asshats, fork the project. Do any of those things, and I'll respect you. Complain and sling personal attacks and I won't.
Those of us that run OSes that don't suck and don't use versions of Python that are older than the average hamster's lifespan can add two lines to their project and be more secure TODAY.
"the django overlords" "Dozens have commit access."
I suspect that's what he meant.
Since you feel that strongly I can expect to see a patch from you fixing the technical issues I mentioned, right? You write it, I'll commit it. Go.
There are things I hate about Git, and I could fix them myself, but I don't submit patches to Git. I just use Mercurial instead.
"Send patches" isn't a be-all, end-all response to any criticism of an open source project.
You did exactly the right thing: figured out how to solve the problem regardless. That's something that'll motivate me; calling me stupid or irresponsible won't.
So yeah, you're right, it is bullshit, and so is the attitude I responded to. Garbage in, garbage out, I suppose.
Here's what I want to know about this in a nutshell:
Does Django (the project as a whole) want to provide the best possible security, within reason, for its contrib.auth module?
If not, why not, and why isn't it stated prominently in the documentation?
Is bcrypt not the best possible security, or reasonably close to it?
If not, why not? I'm not even remotely close to a cryptography expert, so although bcrypt's support for arbitrary work factors seems to provide very good security to me I know I could very well be horribly wrong in this thinking.
Is providing bcrypt hashing for passwords in contrib.auth not within the realm of reasonable effort? This could mean rewriting bcrypt in pure Python and including it in contrib, to support Windows users.
If not, why not? Perhaps rewriting bcrypt in Pure Python is not easy -- I haven't tried it myself.
If bcrypt hashing is secure and reasonable to implement, and Django wants to provide the best security possible (within reason), why is this not a blocking issue for Django 1.3?
I genuinely don't know the answers to any of these questions, so I'd really love to know.
Don't believe me? Your words:
Does Django (the project as a whole) want to provide the best possible security, within reason, for its contrib.auth module? If not, why not, and why isn't it stated prominently in the documentation?
Of course we want to provide the best possible security, within reason. But reasonable people can and do disagree on what's "within reason", and Jacob's outlined some technical hurdles regarding bcrypt which -- so far as I'm aware -- no-one in this thread has bothered to offer solutions for.
If you're genuinely interested in seeing bcrypt in Django, and have constructive suggestions on how to overcome these technical hurdles, then I'm all ears. If, on the other hand, you're just going to post passive-aggressive stuff framed to make us look like we don't really care about security, well, don't expect me or anyone else to fall all over themselves trying to help you out.
I admitted I might be wrong at pretty much any stage, and Jacob's response convinced me that my "rewrite bcrypt in Python" option is probably not reasonable at this point.
How could I have phrased that differently and still asked the same questions?
Well, I don't speak for the project as a whole, so I'm going to just answer personally. I'll try to channel the rest of the core team as best I can, but please don't take any of the below as any sort of "official" thing. I may very well have a different point of view or be in the minority -- I often am, actually.
> Does Django (the project as a whole) want to provide the best possible security, within reason, for its contrib.auth module?
I certainly do, and I'm sure the rest of the team feels similarly. We take security issues very seriously and I'm disappointed we've not been able to demonstrate that through our past actions (i.e. built-in XSS and CSRF protection, our security releases, etc.) This indicates to me that we haven't done a good job being clear about our goals with regard to security. So that's something to work on.
I think, though, that reasonable people can -- and do -- disagree about what "within reason" means. I mean, are we building Django to protect against script kiddies? Malicious employees? Corporate espionage? Government agencies?
Me, I suspect I'd choose to fold in the face of a lawsuit or subpoena, so I don't particularly care if my passwords are safe against the NSA or something. But that's because I'm a spoiled comfortable middle class yutz.
> Is bcrypt not the best possible security, or reasonably close to it?
Personally I have no idea. I'm not a security expert, nor am I even a well-informed amateur. I've read (here and on Reddit, mostly) that bcrypt is the best there is. I've read that bcrypt is for lamers and scrypt is better. I've also been told that salts & sha1 is fine. I've also been told that sha1 will eat my children and burn my house down. I'm honestly not qualified to judge.
Given what I know, my feeling is that bcrypt/scrypt is certainly an improvement over sha1, and probably an improvement over any sha version. I'm not convinced that it's an improvement over, say, multiple rounds of a sha algorithm.
More importantly, I'm not clear on exactly how big a deal this is. There's a spectrum: at one end, we activate our security policy, halt everything, and release new versions, damned the backwards compatibility concerns. At the other end of the spectrum we do nothing. I really don't know where on this spectrum the issue falls. I suspect that it's somewhere a bit more serious than the potential timing attacks we just fixed in trunk, but maybe a bit less serious than the DOS attack our last security release fixed.
> Is providing bcrypt hashing for passwords in contrib.auth not within the realm of reasonable effort? This could mean rewriting bcrypt in pure Python and including it in contrib, to support Windows users.
Of course it's possible, but the devil as they say is in the details. I think it should be possible to support bcrypt if it's installed, but the concerns about data portability need to be addressed in some way. At the very least there should be some "I don't care about data portability" flag you can set to turn on bcrupt support.
A pure-Python bcrypt implementation would certainly help. But I certainly am not going to rewrite bcrypt -- I know enough about crypto to know that I shouldn't be allowed with a thousand miles of writing an algorithm by hand. And frankly there aren't any active committers I'd trust to write such an implementation. It would have to come from a pretty unimpeachable source, wouldn't you agree?
> If bcrypt hashing is secure and reasonable to implement, and Django wants to provide the best security possible (within reason), why is this not a blocking issue for Django 1.3?
Because nobody proposed it and we're well past feature freeze for 1.3 and very close to cutting a release candidate. Also because there's a great third-party app that provides this feature in a very easy-to-use way :)
But If a majority of the community wanted to make bcrypt (or whatever) a blocking feature for 1.3 then I'd go along with it. I'd argue against it, but again I'm just one voice. A loud one, maybe, but I'd like to think I can take being wrong graciously. I was against template auto-escaping originally, for example, so clearly I'e already got a good track record of being wrong about security.
I hope that helps; it's late and I've had a long day. Please ask if I'm not being clear.
I think, though, that reasonable people can -- and do -- disagree about what "within reason" means.
Absolutely, which is where my next questions come from. I'm not convinced that it's an improvement over, say, multiple rounds of a sha algorithm.
Sure, for this conversation feel free to replace "bcrypt" with "configurable rounds of SHA1". More importantly, I'm not clear on exactly how big a deal this is.
I agree here.Yes, bcrypt is better.
Is it "better enough" to warrant backwards-incompatible changes? Maybe not.
I'm not clear on Django's database-compatibility policy though. Are databases created with Django 1.X guaranteed to work with Django 1.Y (where Y < X)? If not, then there are no problems. If so, then you're right, a backwards incompatible change like this is not trivial.
Maybe I completely missed this in the docs.
At the very least there should be some "I don't care about data portability" flag you can set to turn on bcrupt support.
This goes back to my question about databases working with older versions of Django. Did I miss an important part of the docs? A pure-Python bcrypt implementation would certainly help. But I certainly am not going to rewrite bcrypt -- I know enough about crypto to know that I shouldn't be allowed with a thousand miles of writing an algorithm by hand. And frankly there aren't any active committers I'd trust to write such an implementation. It would have to come from a pretty unimpeachable source, wouldn't you agree?
This is the first argument that really convinces me. If you need to support Windows and don't have anyone you trust to write a real crypt implementation in pure Python, that kind of kills the idea in its tracks.A third-party app usable by non-Windows-users seems like the best option.
I hope that helps; it's late and I've had a long day. Please ask if I'm not being clear.
Definitely. Thanks for taking the time to answer.Reasonably close to it, yes, but scrypt is better. scrypt makes the KDF expensive not just in time, but in memory as well.
http://www.tarsnap.com/scrypt.html
"We estimate that on modern (2009) hardware, if 5 seconds are spent computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the cost of a similar attack against bcrypt (to find the same password), and 20000 times greater than a similar attack against PBKDF2."
I don't normally have this sort of attitude, but when it comes to security, "if you can't [be bothered to] do it right, DON'T." MD5/SHA/etc are designed to be fast. That is the absolute last thing you want in a hashing algorithm that you're using for passwords.
py-bcrypt works with 2.4, too.
Aren't warning in documentation "This setting requires this and that, so if you chose it don't expect your project to be easily portable. You have been warned." enough?