Пользователи: аутентификация и пароли

Django приносит готовую систему пользователей: модель User, вход, выход, регистрацию и проверку прав. Не пишите аутентификацию с нуля — это опасно.
Суть: приложение django.contrib.auth даёт модель User, хеширование паролей, функции login/logout/authenticate, декоратор login_required и систему прав. Пароли никогда не хранятся в открытом виде.

Готовая модель User

Аутентификация — критичная по безопасности область, где самодельные решения почти всегда содержат дыры. Django предоставляет проверенную систему. Модель User из django.contrib.auth хранит логин, email, пароль (в виде хеша!), флаги is_active, is_staff, is_superuser и даты. Доступ к текущему пользователю — через request.user.

Вход, выход, проверка

Три функции — основа аутентификации. authenticate проверяет логин и пароль, login создаёт сессию, logout её завершает:

from django.contrib.auth import authenticate, login, logout

def login_view(request):
    if request.method == "POST":
        user = authenticate(
            request,
            username=request.POST["username"],
            password=request.POST["password"],
        )
        if user is not None:
            login(request, user)
            return redirect("home")
    return render(request, "login.html")

Защита страниц

Чтобы закрыть страницу от анонимов, для функций используют декоратор @login_required, для CBV — миксин LoginRequiredMixin:

from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request, "dashboard.html")

Неавторизованного пользователя Django перенаправит на страницу входа. В шаблонах проверяют {% if user.is_authenticated %}.

Как это работает под капотом: пароли

Самое важное: Django никогда не хранит пароли в открытом виде. При регистрации пароль прогоняется через медленную криптографическую хеш-функцию (по умолчанию PBKDF2) с солью. В базе лежит только хеш. При входе введённый пароль хешируется так же и сравнивается с сохранённым. Даже при утечке базы исходные пароли восстановить крайне сложно. Принцип «хешируем при регистрации, сравниваем хеши при входе» — языко-независимый. Вот его упрощённая модель:

# Попробуй сам ▶ — хеширование и проверка пароля (упрощённо)
import hashlib, os

def hash_password(password, salt=None):
    salt = salt or os.urandom(8).hex()
    # PBKDF2: тысячи итераций делают перебор дорогим
    digest = hashlib.pbkdf2_hmac(
        "sha256", password.encode(), salt.encode(), 100_000
    ).hex()
    return f"pbkdf2_sha256$100000${salt}${digest}"

def check_password(password, stored):
    _, _, salt, _ = stored.split("$")
    return hash_password(password, salt) == stored

# регистрация: храним ТОЛЬКО хеш
stored = hash_password("super-secret")
print("В базе лежит:", stored[:40], "...")

# вход: сравниваем хеши, а не пароли
print("Верный пароль:   ", check_password("super-secret", stored))
print("Неверный пароль: ", check_password("wrong", stored))

Django делает это надёжнее (несколько алгоритмов, автоматическое усиление при росте мощности железа), но идея ровно такая: в базе — хеш с солью, а при входе сравниваются хеши.

Сессии

После login Django создаёт сессию: в базе хранится её состояние, а браузеру выдаётся кука с идентификатором. На каждом следующем запросе SessionMiddleware и AuthenticationMiddleware по этой куке восстанавливают request.user. Так пользователь остаётся «залогиненным» между запросами.

Частые ошибки

  • Писать свою аутентификацию и хранить пароли как есть. Это грубейшая уязвимость. Используйте django.contrib.auth.
  • Устанавливать пароль через присваивание поля. Нужен user.set_password(...), иначе пароль не захешируется.
  • Забыть @login_required на приватных страницах. Их увидят анонимы.
  • Путать authenticate и login. Первый проверяет, второй создаёт сессию — нужны оба.

Best practices

  • Никогда не реализуйте хранение паролей сами — доверьте Django.
  • Устанавливайте пароли через set_password, проверяйте через check_password.
  • Защищайте приватные страницы декоратором/миксином, не полагайтесь на «скрытые» URL.
  • Для расширения модели пользователя используйте кастомную AbstractUser с самого начала проекта.

Итоги

Django даёт готовую и безопасную систему пользователей: модель User, функции authenticate/login/logout, защиту страниц через login_required. Пароли хранятся только как хеши с солью, сессии держатся на куках. Никогда не пишите аутентификацию с нуля. Дальше — встроенная защита Django от веб-атак.

Проверьте себя
1. Как Django хранит пароли пользователей?
AВ открытом виде в базе
BВ виде хеша с солью (PBKDF2), исходный пароль не хранится
CВ куке браузера
DВ зашифрованном виде с общим ключом
2. Как закрыть view-функцию от неавторизованных пользователей?
AСпрятать её URL
BПрименить декоратор @login_required
CУдалить из urls.py
DПроверять пароль вручную