Opened 12 years ago

Closed 12 years ago

Last modified 12 years ago

#18157 closed New feature (fixed)

Document that setting PASSWORD_HASHERS for tests can make them much faster

Reported by: Carl Meyer Owned by: nobody
Component: Documentation Version: 1.4
Severity: Normal Keywords:
Cc: kmike84@…, dan.fairs@…, dev@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

As discussed in this thread, the new default PBKDF2 password hasher in Django 1.4 is significantly slower by design than the previous MD5 hasher (so that cracking of passwords in an exposed password database is more time-consuming). It seems that for some test suites that authenticate a lot of users, this can slow down the overall test suite run-time by as much as a factor of two.

The workaround is simple: you can override the PASSWORD_HASHERS setting to something like PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher'], just for tests. Given the speed difference, this workaround is probably worth documenting in the testing docs (with the caveat that of course your test suite then won't reveal any bugs in your real PASSWORD_HASHERS setting).

Change History (12)

comment:1 by Mikhail Korobov, 12 years ago

Cc: kmike84@… added

To make it clear: in my case the cause of 2x slowdown seems to be user creation, not user authentication.

comment:2 by Andy Terra, 12 years ago

Considering this workaround won't really test any password hashes, I wonder if having a dummy hasher would further improve test speeds.

Cheers,
AT

comment:3 by Anssi Kääriäinen, 12 years ago

Triage Stage: UnreviewedAccepted

I believe md5 is really "fast enough" unless you are doing nothing else than hashing alone (haven't benchmarked). There is certain point of not having a dummy-hasher at all in Django.

comment:4 by Carl Meyer, 12 years ago

As akaariai noted in the mailing list thread, it may also make sense to make this change in the test_sqlite settings file for running Django's own test suite.

comment:5 by Anssi Kääriäinen, 12 years ago

I moved the test_sqlite settings file issue to another ticket (#18163) to avoid complicating this ticket with the design decision needed there.

I wonder if it would be a good idea to have "TEST_PASSWORD_HASHER" setting. This setting would move one of the existing PASSWORD_HASHERS to the top of the PASSWORD_HASHERS list.

comment:6 by Dan Fairs, 12 years ago

Cc: dan.fairs@… added

comment:7 by Ryan Kaskel, 12 years ago

Cc: dev@… added

comment:8 by Aymeric Augustin, 12 years ago

I wonder if having a dummy hasher would further improve test speeds.

I don't expect the time taken by the MD5 hasher to be significant. PBKDF2 is slow because it hashes the password thousands of times. MD5 hashes it only once.


I wonder if it would be a good idea to have "TEST_PASSWORD_HASHER" setting.

Not another setting please :)

comment:9 by Claude Paroz <claude@…>, 12 years ago

Resolution: fixed
Status: newclosed

In [17d6cd90299e39823e80a005e7a04bc24ee8af4c]:

Fixed #18157 -- Documented that setting PASSWORD_HASHERS can speed up tests

comment:10 by Marc Tamlyn, 12 years ago

Big +1 on this, but it may be worth mentioning that if you have fixtures generated with a different password hasher you'll get errors if you don't include that hasher. Same speed up can be gained just by putting the MD5 hasher at the top of your existing setting. Obviously we shouldn't be using fixtures but...

comment:11 by Claude Paroz <claude@…>, 12 years ago

In [b0d8085c67884f36c8fb4af5f1fdae844e4700f7]:

Added note about including fixtures hashing algorithm

Refs #18157.

comment:12 by jodym@…, 12 years ago

Note that you CAN'T do this for just one test using @override_settings, because PREFERRED_HASHER is initialized once the first time get_hasher() is called. Ugh.

Workaround:

@override_settings(PASSWORD_HASHERS=(
    'django.contrib.auth.hashers.SHA1PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
))
def test_whatever(self):
    import django.contrib.auth.hashers
    django.contrib.auth.hashers.PREFERRED_HASHER = None
        
    ...
Note: See TracTickets for help on using tickets.
Back to Top