Opened 5 years ago

Closed 5 years ago

Last modified 4 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 Changed 5 years ago by Mikhail Korobov

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 Changed 5 years ago by André Terra

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 Changed 5 years ago by Anssi Kääriäinen

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 Changed 5 years ago by Carl Meyer

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 Changed 5 years ago by Anssi Kääriäinen

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 Changed 5 years ago by Dan Fairs

Cc: dan.fairs@… added

comment:7 Changed 5 years ago by Ryan Kaskel

Cc: dev@… added

comment:8 Changed 5 years ago by Aymeric Augustin

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 Changed 5 years ago by Claude Paroz <claude@…>

Resolution: fixed
Status: newclosed

In [17d6cd90299e39823e80a005e7a04bc24ee8af4c]:

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

comment:10 Changed 5 years ago by Marc Tamlyn

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 Changed 5 years ago by Claude Paroz <claude@…>

In [b0d8085c67884f36c8fb4af5f1fdae844e4700f7]:

Added note about including fixtures hashing algorithm

Refs #18157.

comment:12 Changed 4 years ago by jodym@…

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