Управление паролями является такой сущностью, которую не следует переизобретать без особой необходимости и 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 из базы данных, возвращает True при совпадении и False в противном случае.
Создаёт хэшированный пароль в формате, используемом этим приложением. Принимает один обязательный аргумент: открытый пароль. Опционально, если вам не надо использовать стандартные настройки (первая запись списка PASSWORD_HASHERS), вы можете указать “соль” и алгоритм, который следует использовать для хэширования, Сейчас поддерживаются следующие алгоритмы: 'pbkdf2_sha256', 'pbkdf2_sha1', 'bcrypt_sha256' (см. Использование bcrypt с Django), 'bcrypt', 'sha1', 'md5', 'unsalted_md5' (в целях обратной совместимости) и 'crypt', если соответствующая библиотека установлена в системе. Если аргумент с паролем содержит None, возвращается бесполезный пароль, который никогда не будет пропущен функцией check_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:
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:
Validates whether the password meets a minimum length. The minimum length can be customized with the min_length parameter.
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.
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.
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.
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.
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.
Returns a list of the help texts of all validators. These explain the password requirements to the user.
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.
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:
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.
Mar 31, 2016