Управление паролями является такой сущностью, которую не следует переизобретать без особой необходимости и Django старается предоставить безопасный и гибкий набор инструментов для управления пользовательскими паролями. Этот документ описывает как Django хранит пароли, как может быть настроено их хэширование, а также некоторые утилиты для работы с хэшированными паролями.
См.также
Even though users may use strong passwords, attackers might be able to eavesdrop on their connections. Use HTTPS to avoid sending passwords (or any other sensitive data) over plain HTTP connections because they will be vulnerable to password sniffing.
Django предоставляет гибкую систему хранения паролей и по умолчанию использует PBKDF2.
Атрибут password
объекта User
является строкой следующего формата:
<algorithm>$<iterations>$<salt>$<hash>
Данная строка показывает компоненты, которые используются для хранения пользовательского пароля и разделены знаком доллара, а именно: хэширующий алгоритм, количество итераций алгоритма (work factor), случайная соль и полученный хэш пароля. Алгоритмом может быть любой из ряда однонаправленных хэширующих алгоритмов, которые использует Django; см. далее. Итерации описывают количество применений алгоритма для получения хэша. Соль является случайными данным, а сам хэш получается в результате работы однонаправленной функции.
По умолчанию, Django использует алгоритм PBKDF2 с хэшем SHA256, механизм защиты паролей рекомендованный NIST. Этого должно хватить для большинства пользователей: достаточная защита, требующая большой объём вычислительного времени для взлома.
Тем не менее, в зависимости от ваших требований, вы можете выбрать другой алгоритм или даже реализовать собственный алгоритм, который будет соответствовать вашим требованиям к безопасности. Итак, большинство пользователей не должны думать об этом, если вы сомневаетесь, значит вам это точно не надою В противном случае, прочитайте:
Django выбирает алгоритм для использования в соответствии с указанием переменной конфигурации PASSWORD_HASHERS
. Переменная содержит список классов реализующих алгоритмы хэширования, которые поддерживает Django. Первая запись этого списка (речь о settings.PASSWORD_HASHERS[0]
) будет использоваться для сохранения паролей, а все остальные записи являются проверенными средствами, которые могут быть применены для проверки существующих паролей. Это означает, что вам потребуется использовать другой алгоритм хэширования, вам потребуется просто указать его первым в параметре конфигурации PASSWORD_HASHERS
.
По умолчанию PASSWORD_HASHERS
содержит:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
]
Это означает, что Django будет использовать PBKDF2 для сохранения всех паролей, но будет поддерживать проверку паролей, сохранённых с помощью PBKDF2SHA1, bcrypt, SHA1 и так далее. Следующие несколько разделов описывают ряд общих способов, которые могут быть использованы опытными пользователями для изменения данного параметра конфигурации.
Bcrypt является популярным алгоритмом для хранения паролей, который специально разработан для хранения “долгих” паролей. Данный алгоритм не выбран в качестве стандартного в Django так как он требует использования внешних библиотек, но раз он используется многими, то Django поддерживает его, требуя минимальных усилий по его установке.
Для использования Bcrypt в качестве алгоритма по умолчанию, выполните следующие действия:
Установите bcrypt library. Это можно сделать с помощью команды pip install django[bcrypt]
или скачайте библиотеку и установите её с помощью команды python setup.py install
.
Измените PASSWORD_HASHERS
так, чтобы BCryptSHA256PasswordHasher
был указан первым. То есть, в файле конфигурации надо сделать так:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
]
(Следует сохранить остальные записи в списке, иначе Django не сможет обновлять пароли; см. далее)
Вот так, теперь Django по умолчанию будет использовать Bcrypt в качестве алгоритма хранения паролей.
Обрезание паролей с помощью BCryptPasswordHasher
Разработчики алгоритма bcrypt обрезают все пароли до 72 символов, что означает bcrypt(password_with_100_chars) == bcrypt(password_with_100_chars[:72])
. Оригинальный BCryptPasswordHasher
не использует особую обработку и, следовательно, также имеет аналогичное ограничение на длину пароля. BCryptSHA256PasswordHasher
исправляет это поведение, сначала хэшируя пароль с помощью sha256. Это действие предотвращает обрезание пароля, рекоментуем использовать эту реализацию вместо BCryptPasswordHasher
. Причина применения такого обрезания проста, длина пароля обычного пользователя не превышает 72 символа и, даже будучи обрезанным до 72 символов, такой пароль всё равно требует значительных вычислительных ресурсов для его прямого подбора. Тем не менее, мы рекомендуем использовать BCryptSHA256PasswordHasher
в любом случае по принципу “запас карман не тянет”.
Другие реализации bcrypt
Существует несколько других реализаций алгоритма, которые позволяют использовать bcrypt в Django. Django не поддерживает из из коробки. Для активации поддержки, вам потребуется привести хэши в вашей базе данных к виду bcrypt$(raw bcrypt output)
. Например: bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy
.
Алгоритмы PBKDF2 и bcrypt используют ряд итераций или округлений для хэшей. Это значительно замедляют действия атакующих, усложняя выполнение атаки на хэшированные пароли. Однако, по мере увеличения вычислительной мощности, количество этих итераций следует увеличивать. Мы установили достаточное значение по умолчанию (и будем его увеличивать с каждым новым релизом Django), но вы можете пожелать увиличить или уменьшить это значение самостоятельно, в зависимости от вашей политики безопасности и вычислительной мощности, имеющейся в наличии. Чтобы сделать это, следует унаследоваться от класса нужного алгоритма и переопределить параметры iterations
. Например, для увеличения количества итераций в алгоритме PBKDF2:
Унаследуйтесь от django.contrib.auth.hashers.PBKDF2PasswordHasher
:
from django.contrib.auth.hashers import PBKDF2PasswordHasher
class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
"""
A subclass of PBKDF2PasswordHasher that uses 100 times more iterations.
"""
iterations = PBKDF2PasswordHasher.iterations * 100
Сохраните это в ваш проект. Например, вы можете разместить это в файле подобном myproject/hashers.py
.
Добавьте новый алгоритм хэширования в начало списка конфигурационного параметра PASSWORD_HASHERS
:
PASSWORD_HASHERS = [
'myproject.hashers.MyPBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
]
Вот так, теперь Django будет использовать большее количество итераций при сохранении паролей с помощью PBKDF2.
При аутентификации пользователей, если их пароли сохранены с помощью алгоритма, отличающегося от стандартного, то Django будет автоматически применять стандартный алгоритм хэширования. Это означает, что старые установки Django автоматически получат обновление в области аутентификации пользователей, и это также означает, что вы можете переключаться на новые (и более лучшие) алгоритмы хранения паролей по мере их изобретения.
However, Django can only upgrade passwords that use algorithms mentioned in
PASSWORD_HASHERS
, so as you upgrade to new systems you should make
sure never to remove entries from this list. If you do, users using
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.
Passwords updates when changing the number of bcrypt rounds was added.
Модуль django.contrib.auth.hashers
предоставляет набор функций для создания и проверки хэшированных паролей. Вы можете использовать эти функции независимо от модели ``User``l.
check_password
(password, encoded)¶Если требуется вручную аутентифицировать пользователя с помощью сравнения открытого пароля с захэшированным паролем из базы данных, используйте вспомогательную функцию check_password()
. Она принимает два аргумента: открытый пароль и полное значение поля password
из базы данных, возвращает True
при совпадении и False
в противном случае.
make_password
(password, salt=None, hasher='default')¶Создаёт хэшированный пароль в формате, используемом этим приложением. Принимает один обязательный аргумент: открытый пароль. Опционально, если вам не надо использовать стандартные настройки (первая запись списка PASSWORD_HASHERS
), вы можете указать “соль” и алгоритм, который следует использовать для хэширования, Сейчас поддерживаются следующие алгоритмы: 'pbkdf2_sha256'
, 'pbkdf2_sha1'
, 'bcrypt_sha256'
(см. Использование bcrypt с Django), 'bcrypt'
, 'sha1'
, 'md5'
, 'unsalted_md5'
(в целях обратной совместимости) и 'crypt'
, если соответствующая библиотека установлена в системе. Если аргумент с паролем содержит None
, возвращается бесполезный пароль, который никогда не будет пропущен функцией check_password()
.
is_password_usable
(encoded_password)¶Проверяет, является ли переданная строка хэшированным паролем, который имеет шанс пройти проверку с помощью функции check_password()
.
Users often choose poor passwords. To help mitigate this problem, Django offers pluggable password validation. You can configure multiple password validators at the same time. A few validators are included in Django, but it’s simple to write your own as well.
Each password validator must provide a help text to explain the requirements to the user, validate a given password and return an error message if it does not meet the requirements, and optionally receive passwords that have been set. Validators can also have optional settings to fine tune their behavior.
Validation is controlled by the AUTH_PASSWORD_VALIDATORS
setting.
By default, validators are used in the forms to reset or change passwords.
The default for the setting is an empty list, which means no validators are
applied. In new projects created with the default startproject
template, a simple set of validators is enabled.
Примечание
Password validation can prevent the use of many types of weak passwords. However, the fact that a password passes all the validators doesn’t guarantee that it is a strong password. There are many factors that can weaken a password that are not detectable by even the most advanced password validators.
Password validation is configured in the
AUTH_PASSWORD_VALIDATORS
setting:
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 9,
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
This example enables all four included validators:
UserAttributeSimilarityValidator
, which checks the similarity between
the password and a set of attributes of the user.MinimumLengthValidator
, which simply checks whether the password meets a
minimum length. This validator is configured with a custom option: it now
requires the minimum length to be nine characters, instead of the default
eight.CommonPasswordValidator
, which checks whether the password occurs in a
list of common passwords. By default, it compares to an included list of
1000 common passwords.NumericPasswordValidator
, which checks whether the password isn’t
entirely numeric.For UserAttributeSimilarityValidator
and CommonPasswordValidator
,
we’re simply using the default settings in this example.
NumericPasswordValidator
has no settings.
The help texts and any errors from password validators are always returned in
the order they are listed in AUTH_PASSWORD_VALIDATORS
.
Django includes four validators:
MinimumLengthValidator
(min_length=8)¶Validates whether the password meets a minimum length.
The minimum length can be customized with the min_length
parameter.
UserAttributeSimilarityValidator
(user_attributes=DEFAULT_USER_ATTRIBUTES, max_similarity=0.7)¶Validates whether the password is sufficiently different from certain attributes of the user.
The user_attributes
parameter should be an iterable of names of user
attributes to compare to. If this argument is not provided, the default
is used: 'username', 'first_name', 'last_name', 'email'
.
Attributes that don’t exist are ignored.
The maximum similarity the password can have, before it is rejected, can
be set with the max_similarity
parameter, on a scale of 0 to 1.
A setting of 0 will cause all passwords to be rejected, whereas a setting
of 1 will cause it to only reject passwords that are identical to an
attribute’s value.
CommonPasswordValidator
(password_list_path=DEFAULT_PASSWORD_LIST_PATH)¶Validates whether the password is not a common password. By default, this checks against a list of 1000 common password created by Mark Burnett.
The password_list_path
can be set to the path of a custom file of
common passwords. This file should contain one password per line and
may be plain text or gzipped.
NumericPasswordValidator
¶Validates whether the password is not entirely numeric.
There are a few functions in django.contrib.auth.password_validation
that
you can call from your own forms or other code to integrate password
validation. This can be useful if you use custom forms for password setting,
or if you have API calls that allow passwords to be set, for example.
validate_password
(password, user=None, password_validators=None)¶Validates a password. If all validators find the password valid, returns
None
. If one or more validators reject the password, raises a
ValidationError
with all the error messages
from the validators.
The user
object is optional: if it’s not provided, some validators may
not be able to perform any validation and will accept any password.
password_changed
(password, user=None, password_validators=None)¶Informs all validators that the password has been changed. This can be used by validators such as one that prevents password reuse. This should be called once the password has been successfully changed.
For subclasses of AbstractBaseUser
,
the password field will be marked as “dirty” when calling
set_password()
which
triggers a call to password_changed()
after the user is saved.
password_validators_help_texts
(password_validators=None)¶Returns a list of the help texts of all validators. These explain the password requirements to the user.
password_validators_help_text_html
(password_validators=None)¶Returns an HTML string with all help texts in an <ul>
. This is
helpful when adding password validation to forms, as you can pass the
output directly to the help_text
parameter of a form field.
get_password_validators
(validator_config)¶Returns a set of validator objects based on the validator_config
parameter. By default, all functions use the validators defined in
AUTH_PASSWORD_VALIDATORS
, but by calling this function with an
alternate set of validators and then passing the result into the
password_validators
parameter of the other functions, your custom set
of validators will be used instead. This is useful when you have a typical
set of validators to use for most scenarios, but also have a special
situation that requires a custom set. If you always use the same set
of validators, there is no need to use this function, as the configuration
from AUTH_PASSWORD_VALIDATORS
is used by default.
The structure of validator_config
is identical to the
structure of AUTH_PASSWORD_VALIDATORS
. The return value of
this function can be passed into the password_validators
parameter
of the functions listed above.
Note that where the password is passed to one of these functions, this should always be the clear text password - not a hashed password.
If Django’s built-in validators are not sufficient, you can write your own password validators. Validators are fairly simple classes. They must implement two methods:
validate(self, password, user=None)
: validate a password. Return
None
if the password is valid, or raise a
ValidationError
with an error message if the
password is not valid. You must be able to deal with user
being
None
- if that means your validator can’t run, simply return None
for no error.get_help_text()
: provide a help text to explain the requirements to
the user.Any items in the OPTIONS
in AUTH_PASSWORD_VALIDATORS
for your
validator will be passed to the constructor. All constructor arguments should
have a default value.
Here’s a basic example of a validator, with one optional setting:
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
class MinimumLengthValidator(object):
def __init__(self, min_length=8):
self.min_length = min_length
def validate(self, password, user=None):
if len(password) < self.min_length:
raise ValidationError(
_("This password must contain at least %(min_length)d characters."),
code='password_too_short',
params={'min_length': self.min_length},
)
def get_help_text(self):
return _(
"Your password must contain at least %(min_length)d characters."
% {'min_length': self.min_length}
)
You can also implement password_changed(password, user=None
), which will
be called after a successful password change. That can be used to prevent
password reuse, for example. However, if you decide to store a user’s previous
passwords, you should never do so in clear text.
Jun 05, 2017