mirror of
https://github.com/django/django.git
synced 2025-10-24 14:16:09 +00:00
Fixed CVE-2016-2513 -- Fixed user enumeration timing attack during login.
This is a security fix.
This commit is contained in:
committed by
Tim Graham
parent
c5544d2892
commit
67b46ba701
@@ -22,6 +22,39 @@ redirecting to this URL sends the user to ``attacker.com``.
|
||||
Also, if a developer relies on ``is_safe_url()`` to provide safe redirect
|
||||
targets and puts such a URL into a link, they could suffer from an XSS attack.
|
||||
|
||||
CVE-2016-2513: User enumeration through timing difference on password hasher work factor upgrade
|
||||
================================================================================================
|
||||
|
||||
In each major version of Django since 1.6, the default number of iterations for
|
||||
the ``PBKDF2PasswordHasher`` and its subclasses has increased. This improves
|
||||
the security of the password as the speed of hardware increases, however, it
|
||||
also creates a timing difference between a login request for a user with a
|
||||
password encoded in an older number of iterations and login request for a
|
||||
nonexistent user (which runs the default hasher's default number of iterations
|
||||
since Django 1.6).
|
||||
|
||||
This only affects users who haven't logged in since the iterations were
|
||||
increased. The first time a user logs in after an iterations increase, their
|
||||
password is updated with the new iterations and there is no longer a timing
|
||||
difference.
|
||||
|
||||
The new ``BasePasswordHasher.harden_runtime()`` method allows hashers to bridge
|
||||
the runtime gap between the work factor (e.g. iterations) supplied in existing
|
||||
encoded passwords and the default work factor of the hasher. This method
|
||||
is implemented for ``PBKDF2PasswordHasher`` and ``BCryptPasswordHasher``.
|
||||
The number of rounds for the latter hasher hasn't changed since Django 1.4, but
|
||||
some projects may subclass it and increase the work factor as needed.
|
||||
|
||||
A warning will be emitted for any :ref:`third-party password hashers that don't
|
||||
implement <write-your-own-password-hasher>` a ``harden_runtime()`` method.
|
||||
|
||||
If you have different password hashes in your database (such as SHA1 hashes
|
||||
from users who haven't logged in since the default hasher switched to PBKDF2
|
||||
in Django 1.4), the timing difference on a login request for these users may be
|
||||
even greater and this fix doesn't remedy that difference (or any difference
|
||||
when changing hashers). You may be able to :ref:`upgrade those hashes
|
||||
<wrapping-password-hashers>` to prevent a timing attack for that case.
|
||||
|
||||
Bugfixes
|
||||
========
|
||||
|
||||
|
||||
@@ -22,6 +22,39 @@ redirecting to this URL sends the user to ``attacker.com``.
|
||||
Also, if a developer relies on ``is_safe_url()`` to provide safe redirect
|
||||
targets and puts such a URL into a link, they could suffer from an XSS attack.
|
||||
|
||||
CVE-2016-2513: User enumeration through timing difference on password hasher work factor upgrade
|
||||
================================================================================================
|
||||
|
||||
In each major version of Django since 1.6, the default number of iterations for
|
||||
the ``PBKDF2PasswordHasher`` and its subclasses has increased. This improves
|
||||
the security of the password as the speed of hardware increases, however, it
|
||||
also creates a timing difference between a login request for a user with a
|
||||
password encoded in an older number of iterations and login request for a
|
||||
nonexistent user (which runs the default hasher's default number of iterations
|
||||
since Django 1.6).
|
||||
|
||||
This only affects users who haven't logged in since the iterations were
|
||||
increased. The first time a user logs in after an iterations increase, their
|
||||
password is updated with the new iterations and there is no longer a timing
|
||||
difference.
|
||||
|
||||
The new ``BasePasswordHasher.harden_runtime()`` method allows hashers to bridge
|
||||
the runtime gap between the work factor (e.g. iterations) supplied in existing
|
||||
encoded passwords and the default work factor of the hasher. This method
|
||||
is implemented for ``PBKDF2PasswordHasher`` and ``BCryptPasswordHasher``.
|
||||
The number of rounds for the latter hasher hasn't changed since Django 1.4, but
|
||||
some projects may subclass it and increase the work factor as needed.
|
||||
|
||||
A warning will be emitted for any :ref:`third-party password hashers that don't
|
||||
implement <write-your-own-password-hasher>` a ``harden_runtime()`` method.
|
||||
|
||||
If you have different password hashes in your database (such as SHA1 hashes
|
||||
from users who haven't logged in since the default hasher switched to PBKDF2
|
||||
in Django 1.4), the timing difference on a login request for these users may be
|
||||
even greater and this fix doesn't remedy that difference (or any difference
|
||||
when changing hashers). You may be able to :ref:`upgrade those hashes
|
||||
<wrapping-password-hashers>` to prevent a timing attack for that case.
|
||||
|
||||
Bugfixes
|
||||
========
|
||||
|
||||
|
||||
@@ -186,6 +186,14 @@ unmentioned algorithms won't be able to upgrade. Hashed passwords will be
|
||||
updated when increasing (or decreasing) the number of PBKDF2 iterations or
|
||||
bcrypt rounds.
|
||||
|
||||
Be aware that if all the passwords in your database aren't encoded in the
|
||||
default hasher's algorithm, you may be vulnerable to a user enumeration timing
|
||||
attack due to a difference between the duration of a login request for a user
|
||||
with a password encoded in a non-default algorithm and the duration of a login
|
||||
request for a nonexistent user (which runs the default hasher). You may be able
|
||||
to mitigate this by :ref:`upgrading older password hashes
|
||||
<wrapping-password-hashers>`.
|
||||
|
||||
.. versionchanged:: 1.9
|
||||
|
||||
Passwords updates when changing the number of bcrypt rounds was added.
|
||||
@@ -310,6 +318,29 @@ The corresponding algorithm names are:
|
||||
* ``unsalted_md5``
|
||||
* ``crypt``
|
||||
|
||||
.. _write-your-own-password-hasher:
|
||||
|
||||
Writing your own hasher
|
||||
-----------------------
|
||||
|
||||
.. versionadded:: 1.9.3
|
||||
|
||||
If you write your own password hasher that contains a work factor such as a
|
||||
number of iterations, you should implement a
|
||||
``harden_runtime(self, password, encoded)`` method to bridge the runtime gap
|
||||
between the work factor supplied in the ``encoded`` password and the default
|
||||
work factor of the hasher. This prevents a user enumeration timing attack due
|
||||
to difference between a login request for a user with a password encoded in an
|
||||
older number of iterations and a nonexistent user (which runs the default
|
||||
hasher's default number of iterations).
|
||||
|
||||
Taking PBKDF2 as example, if ``encoded`` contains 20,000 iterations and the
|
||||
hasher's default ``iterations`` is 30,000, the method should run ``password``
|
||||
through another 10,000 iterations of PBKDF2.
|
||||
|
||||
If your hasher doesn't have a work factor, implement the method as a no-op
|
||||
(``pass``).
|
||||
|
||||
Manually managing a user's password
|
||||
===================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user