Управление паролями в Django

Управление паролями является такой сущностью, которую не следует переизобретать без особой необходимости и Django старается предоставить безопасный и гибкий набор инструментов для управления пользовательскими паролями. Этот документ описывает как Django хранит пароли, как может быть настроено их хэширование, а также некоторые утилиты для работы с хэшированными паролями.

Как Django хранит пароли

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

Bcrypt является популярным алгоритмом для хранения паролей, который специально разработан для хранения “долгих” паролей. Данный алгоритм не выбран в качестве стандартного в Django так как он требует использования внешних библиотек, но раз он используется многими, то Django поддерживает его, требуя минимальных усилий по его установке.

Для использования Bcrypt в качестве алгоритма по умолчанию, выполните следующие действия:

  1. Установите bcrypt library. Это можно сделать с помощью команды pip install django[bcrypt] или скачайте библиотеку и установите её с помощью команды python setup.py install.

  2. Измените 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:

  1. Унаследуйтесь от 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.

  2. Добавьте новый алгоритм хэширования в начало списка конфигурационного параметра 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 автоматически получат обновление в области аутентификации пользователей, и это также означает, что вы можете переключаться на новые (и более лучшие) алгоритмы хранения паролей по мере их изобретения.

Тем не менее, Django может обновлять хэш только тех паролей, которые были захэшированы с помощью алгоритмов, перечисленных в параметре конфигурации PASSWORD_HASHERS. Таким образом, при переходе на новый алгоритм хэширования следует удостовериться, что старые алгоритмы не были удалены из списка алгоритмов. В противном случае, пароли пользователей захэшированные неупомянутым в параметре конфигурации алгоритмом не будут обновлены. Пароли также обновляются при изменении количества итераций у алгоритма PBKDF2.

Ручное управление паролями пользователей

Модуль 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().