Spec-Zone.ru › Django 5.1

Как использовать сессии

Django предоставляет полную поддержку анонимных сессий. Система сессий позволяет хранить и извлекать произвольные данные для каждого посетителя сайта. Данные хранятся на стороне сервера, а отправка и получение куки-файлов абстрагированы. Куки-файлы содержат идентификатор сессии, а не сами данные (если вы не используете базовый класс на основе куки).

Включение сессий

Сессии реализованы с помощью модуля middleware.

Для включения функциональности сессий выполните следующие действия:

  • Отредактируйте настройку MIDDLEWARE и убедитесь, что она содержит 'django.contrib.sessions.middleware.SessionMiddleware'. По умолчанию settings.py созданный django-admin startproject имеет SessionMiddleware включенным.

Если вы не хотите использовать сессии, можно удалить строку SessionMiddleware из MIDDLEWARE и 'django.contrib.sessions' из INSTALLED_APPS. Это сэкономит небольшое количество ресурсов.

Настройка движка сессий

По умолчанию Django хранит сессии в вашей базе данных (используя модель django.contrib.sessions.models.Session). Хотя это удобно, в некоторых случаях быстрее хранить данные сессии в другом месте, поэтому Django можно настроить на хранение данных сессии на файловой системе или в кэше.

Использование сессий, поддерживаемых базой данных

Если вы хотите использовать сессии, поддерживаемые базой данных, необходимо добавить 'django.contrib.sessions' в настройку INSTALLED_APPS.

После настройки вашей установки запустите manage.py migrate для установки единственной таблицы базы данных, которая хранит данные сессий.

Использование кэшированных сессий

Для повышения производительности вы можете использовать кэшированный backend сессий.

Чтобы хранить данные сессий с помощью системы кэширования Django, сначала убедитесь, что вы настроили свой кэш; см. документацию по кэшу для получения подробностей.

Предупреждение

Вы должны использовать кэшированные сессии только в том случае, если используете кэш Memcached или Redis. Кэш локальной памяти не сохраняет данные достаточно долго, чтобы быть хорошим выбором, и быстрее использовать сессии файла или базы данных напрямую, вместо того, чтобы отправлять все через кэш файла или базы данных. Кроме того, кэш локальной памяти НЕ безопасен для нескольких процессов, поэтому, вероятно, не является хорошим выбором для производственных сред.

Если в CACHES определено несколько кэшей, Django будет использовать кэш по умолчанию. Чтобы использовать другой кэш, установите SESSION_CACHE_ALIAS в имя этого кэша.

После настройки кэша вам необходимо выбрать между кэшированной базой данных или неперсистентным кэшем.

Кэшированный backend базы данных (cached_db) использует проходящий через кэш – записи сессий применяются и в базу данных, и в кэш в указанном порядке. Если запись в кэш завершится ошибкой, исключение обрабатывается и регистрируется с помощью журнала сессий, чтобы избежать сбоя успешной операции записи.

Изменено в Django 5.1:

Была добавлена обработка и регистрация исключений при записи в кэш.

Чтение сессий происходит из кэша или из базы данных, если данные были удалены из кэша. Чтобы использовать этот backend, установите SESSION_ENGINE на "django.contrib.sessions.backends.cached_db", и следуйте инструкциям по настройке для использования сессий, поддерживаемых базой данных.

Backend кэша (cache) хранит данные сессий только в вашем кэше. Это быстрее, потому что избегает сохранения в базе данных, но вам нужно будет учесть, что произойдет, когда данные кэша будут удалены. Удаление может произойти, если кэш заполнится или сервер кэша перезапустится, что приведет к потере данных сессий, в том числе к выходу из системы пользователей. Чтобы использовать этот backend, установите SESSION_ENGINE на "django.contrib.sessions.backends.cache".

Кэшированный backend может быть сделан постоянным с помощью постоянного кэша, например, Redis с соответствующей конфигурацией. Но если ваш кэш точно настроен на достаточную постоянность, используйте кэшированный backend базы данных. Это позволит избежать особых случаев, вызванных ненадежным хранением данных в производственной среде.

Использование сессий на основе файлов

Чтобы использовать сессии на основе файлов, установите настройку SESSION_ENGINE на "django.contrib.sessions.backends.file".

Возможно, также потребуется установить настройку SESSION_FILE_PATH (по умолчанию она выводится из tempfile.gettempdir(), скорее всего, /tmp) для управления тем, где Django хранит файлы сессий. Убедитесь, что ваш веб-сервер имеет разрешения на чтение и запись в этом расположении.

Использование сессий на основе куки

Чтобы использовать сессии на основе куки, установите настройку SESSION_ENGINE на "django.contrib.sessions.backends.signed_cookies". Данные сессий будут храниться с помощью инструментов Django для криптографического шифрования и настройки SECRET_KEY.

Примечание

Рекомендуется оставить настройку SESSION_COOKIE_HTTPONLY в True для предотвращения доступа к хранящимся данным из JavaScript.

Предупреждение

Данные сессии подписаны, но не зашифрованы

При использовании backend куки клиент может прочитать данные сессии.

Используется MAC (код аутентификации сообщения) для защиты данных от изменений со стороны клиента, так что данные сессии будут аннулированы при подделке. Такое же аннулирование происходит, если клиент, хранящий куки (например, браузер вашего пользователя), не может сохранить все куки сессии и теряет данные. Несмотря на то, что Django сжимает данные, вполне возможно превысить обычное ограничение в 4096 байт на куки.

Гарантия свежести отсутствует

Обратите также внимание, что, хотя MAC может гарантировать аутентичность данных (что они были сгенерированы вашим сайтом, а не кем-то другим) и целостность данных (что все данные присутствуют и корректны), он не может гарантировать свежесть, т.е. что вам возвращаются последние данные, отправленные клиенту. Это означает, что для некоторых применений данных сессий backend куки может подвергнуть вас атакам повторного использования. В отличие от других backends сессий, которые хранят запись каждой сессии на сервере и аннулируют ее при выходе пользователя, сессии на основе куки не аннулируются при выходе пользователя. Таким образом, если злоумышленник украдет куки пользователя, он может войти под учетной записью этого пользователя, даже если пользователь вышел из системы. Куки будут считаться устаревшими только если они старше, чем значение SESSION_COOKIE_AGE.

Производительность

Размер куки может повлиять на скорость вашего сайта.

Использование сессий в представлениях

Когда SessionMiddleware активирован, каждый объект HttpRequest — первый аргумент любой функции представления Django — будет иметь атрибут session, который является объектом, похожим на словарь.

Вы можете читать и записывать в request.session в любой момент в вашем представлении. Вы можете редактировать его несколько раз.

class backends.base.SessionBase

Это базовый класс для всех объектов сессии. Он имеет следующие стандартные методы словаря:

__getitem__(key)

Пример: fav_color = request.session['fav_color']

__setitem__(key, value)

Пример: request.session['fav_color'] = 'blue'

__delitem__(key)

Пример: del request.session['fav_color']. Это вызывает KeyError если заданный key не содержится в сессии.

__contains__(key)

Пример: 'fav_color' in request.session

get(key, default=None)
aget(key, default=None)

Асинхронная версия: aget()

Пример: fav_color = request.session.get('fav_color', 'red')

Изменено в Django 5.1:

aget() функция была добавлена.

aset(key, value)
Добавлена в Django 5.1.

Пример: await request.session.aset('fav_color', 'red')

update(dict)
aupdate(dict)

Асинхронная версия: aupdate()

Пример: request.session.update({'fav_color': 'red'})

Изменено в Django 5.1:

aupdate() функция была добавлена.

pop(key, default=__not_given)
apop(key, default=__not_given)

Асинхронная версия: apop()

Пример: fav_color = request.session.pop('fav_color', 'blue')

Изменено в Django 5.1:

apop() функция была добавлена.

keys()
akeys()

Асинхронная версия: akeys()

Изменено в Django 5.1:

akeys() функция была добавлена.

values()
avalues()

Асинхронная версия: avalues()

Изменено в Django 5.1:

avalues() функция была добавлена.

has_key(key)
ahas_key(key)

Асинхронная версия: ahas_key()

Изменено в Django 5.1:

ahas_key() функция была добавлена.

items()
aitems()

Асинхронная версия: aitems()

Изменено в Django 5.1:

aitems() функция была добавлена.

setdefault()
asetdefault()

Асинхронная версия: asetdefault()

Изменено в Django 5.1:

asetdefault() функция была добавлена.

clear()

Также есть эти методы:

flush()
aflush()

Асинхронная версия: aflush()

Удаляет текущие данные сессии из сессии и удаляет cookie сессии. Это используется, если вы хотите убедиться, что к предыдущим данным сессии больше нельзя получить доступ из браузера пользователя (например, функция django.contrib.auth.logout() вызывает её).

Изменено в Django 5.1:

aflush() функция была добавлена.

set_test_cookie()
aset_test_cookie()

Асинхронная версия: aset_test_cookie()

Устанавливает тестовый cookie, чтобы определить, поддерживает ли браузер пользователя cookies. Из-за того, как работают cookies, вы не сможете проверить это до следующего запроса страницы пользователя. Подробнее см. Настройка тестовых cookies ниже.

Изменено в Django 5.1:

aset_test_cookie() функция была добавлена.

test_cookie_worked()
atest_cookie_worked()

Асинхронная версия: atest_cookie_worked()

Возвращает либо True или False, в зависимости от того, принял ли браузер пользователя тестовый cookie. Из-за того, как работают cookies, вам нужно будет вызвать set_test_cookie() или aset_test_cookie() в предыдущем, отдельном запросе страницы. Подробнее см. Настройка тестовых cookies ниже.

Изменено в Django 5.1:

atest_cookie_worked() функция была добавлена.

delete_test_cookie()
adelete_test_cookie()

Асинхронная версия: adelete_test_cookie()

Удаляет тестовый cookie. Используйте это, чтобы очистить после себя.

Изменено в Django 5.1:

adelete_test_cookie() функция была добавлена.

get_session_cookie_age()

Возвращает значение настройки SESSION_COOKIE_AGE. Это можно переопределить в пользовательском бэкенде сессий.

set_expiry(value)
aset_expiry(value)

Асинхронная версия: aset_expiry()

Устанавливает время истечения срока действия сессии. Вы можете передать разные значения:

  • Если value — целое число, сессия истечёт через указанное количество секунд бездействия. Например, вызов request.session.set_expiry(300) заставит сессию истечь через 5 минут.
  • Если value — объект типа datetime или timedelta, сессия истечёт в указанную дату/время.
  • Если value — 0, cookie сессии пользователя истечёт при закрытии веб-браузера пользователя.
  • Если value — None, сессия вернётся к использованию глобальной политики истечения срока действия сессии.

Чтение сессии не считается деятельностью для целей истечения срока действия. Истечение срока действия сессии вычисляется с момента последнего изменения сессии.

Изменено в Django 5.1:

aset_expiry() функция была добавлена.

get_expiry_age()
aget_expiry_age()

Асинхронная версия: aget_expiry_age()

Возвращает количество секунд до истечения срока действия этого сеанса. Для сеансов без пользовательского срока действия (или тех, которые должны истекать при закрытии браузера), это значение будет равно SESSION_COOKIE_AGE.

Эта функция принимает два необязательных ключевых аргумента:

  • modification: последнее изменение сеанса, как объект datetime. По умолчанию — текущее время.
  • expiry: информация о сроке действия сеанса, как объект datetime, целое число (в секундах) или None. По умолчанию — значение, сохраненное в сеансе методом set_expiry()/aset_expiry(), если он есть, или None.

Примечание

Этот метод используется сеансовыми бэкендами для определения срока действия сеанса в секундах при сохранении сеанса. Он не предназначен для использования вне этого контекста.

В частности, хотя возможно определить оставшийся срок действия сеанса только когда у вас есть правильное значение modification и expiry установлено как объект datetime, где у вас есть значение modification, проще вручную рассчитать срок действия:

expires_at = modification + timedelta(seconds=settings.SESSION_COOKIE_AGE)
Изменено в Django 5.1:

aget_expiry_age() функция была добавлена.

get_expiry_date()
aget_expiry_date()

Асинхронная версия: aget_expiry_date()

Возвращает дату истечения срока действия этого сеанса. Для сеансов без пользовательского срока действия (или тех, которые должны истекать при закрытии браузера), это значение будет равно дате, наступившей через SESSION_COOKIE_AGE секунд от текущего момента.

Эта функция принимает те же ключевые аргументы, что и get_expiry_age(), и аналогичные замечания по использованию применяются.

Изменено в Django 5.1:

aget_expiry_date() функция была добавлена.

get_expire_at_browser_close()
aget_expire_at_browser_close()

Асинхронная версия: aget_expire_at_browser_close()

Возвращает значение True или False, в зависимости от того, истекает ли cookie сеанса пользователя при закрытии веб-браузера.

Изменено в Django 5.1:

aget_expire_at_browser_close() функция была добавлена.

clear_expired()
aclear_expired()

Асинхронная версия: aclear_expired()

Удаляет истекшие сеансы из хранилища сеансов. Этот метод класса вызывается clearsessions.

Изменено в Django 5.1:

aclear_expired() функция была добавлена.

cycle_key()
acycle_key()

Асинхронная версия: acycle_key()

Создаёт новый ключ сеанса, сохраняя при этом данные текущего сеанса. django.contrib.auth.login() вызывает этот метод для защиты от фиксации сеанса.

Изменено в Django 5.1:

acycle_key() функция была добавлена.

Сериализация сеансов

По умолчанию Django сериализует данные сеанса с помощью JSON. Вы можете использовать настройку SESSION_SERIALIZER для настройки формата сериализации сеансов. Несмотря на замечания, описанные в Создайте собственный сериализатор, мы настоятельно рекомендуем использовать JSON-сериализацию, особенно если вы используете бэкенд cookie.

Например, вот сценарий атаки, если вы используете pickle для сериализации данных сеанса. Если вы используете бэкенд подписанных cookie сеансов и SECRET_KEY (или любой ключ из SECRET_KEY_FALLBACKS) известен злоумышленнику (в Django нет уязвимости, которая бы вызывала утечку ключа), злоумышленник мог бы вставить строку в свой сеанс, который при разборке выполнит произвольный код на сервере. Методика довольно проста и легко доступна в интернете. Хотя хранение сеансов в cookie подписано, чтобы предотвратить подделку, утечка SECRET_KEY сразу же приводит к уязвимости удалённого исполнения кода.

Встроенные сериализаторы

class serializers.JSONSerializer

Обёртка над JSON-сериализатором из django.core.signing. Может сериализовать только базовые типы данных.

Кроме того, поскольку JSON поддерживает только строковые ключи, обратите внимание, что использование нестроковых ключей в request.session не будет работать так, как ожидается:

>>> # initial assignment
>>> request.session[0] = "bar"
>>> # subsequent requests following serialization & deserialization
>>> # of session data
>>> request.session[0]  # KeyError
>>> request.session["0"]
'bar'

Аналогично, данные, которые нельзя закодировать в JSON, такие как байты, не являющиеся UTF-8, такие как '\xd9' (что приводит к UnicodeDecodeError), не могут быть сохранены.

См. раздел Создайте собственный сериализатор для получения дополнительной информации о ограничениях JSON-сериализации.

Создайте собственный сериализатор

Обратите внимание, что JSONSerializer не может обрабатывать произвольные типы данных Python. Как часто бывает, существует компромисс между удобством и безопасностью. Если вы хотите сохранить более сложные типы данных, включая datetime и Decimal в сеансах на основе JSON, вам нужно будет создать собственный сериализатор (или преобразовать такие значения в сериализуемые в JSON объекты перед их сохранением в request.session). Хотя сериализация этих значений часто проста (DjangoJSONEncoder может быть полезен), создание декодера, который надёжно восстановит то, что было введено, более хрупко. Например, вы рискуете получить datetime , который фактически был строкой, которая просто оказалась в том же формате, что и datetime.

Ваш класс сериализатора должен реализовывать два метода, dumps(self, obj) и loads(self, data), для сериализации и десериализации словаря данных сеанса соответственно.

Рекомендации по работе с объектом сеанса

  • Используйте обычные строки Python в качестве ключей словаря в request.session. Это скорее конвенция, чем жёсткое правило.
  • Ключи словаря сеанса, начинающиеся с нижнего подчёркивания, зарезервированы для внутреннего использования Django.
  • Не перезаписывайте request.session новым объектом и не обращайтесь к его атрибутам или не устанавливайте их. Используйте его как словарь Python.

Примеры

Этот упрощённый вид устанавливает переменную has_commented в значение True после отправки комментария пользователем. Он не позволяет пользователю отправлять один и тот же комментарий несколько раз:

def post_comment(request, new_comment):
    if request.session.get("has_commented", False):
        return HttpResponse("You've already commented.")
    c = comments.Comment(comment=new_comment)
    c.save()
    request.session["has_commented"] = True
    return HttpResponse("Thanks for your comment!")

Этот упрощённый вид осуществляет вход «члена» сайта:

def login(request):
    m = Member.objects.get(username=request.POST["username"])
    if m.check_password(request.POST["password"]):
        request.session["member_id"] = m.id
        return HttpResponse("You're logged in.")
    else:
        return HttpResponse("Your username and password didn't match.")

…И этот вид осуществляет выход члена, согласно login() выше:

def logout(request):
    try:
        del request.session["member_id"]
    except KeyError:
        pass
    return HttpResponse("You're logged out.")

Стандартная функция django.contrib.auth.logout() фактически делает немного больше, чем это, чтобы предотвратить случайную утечку данных. Она вызывает метод flush() объекта request.session. Мы используем этот пример для демонстрации работы с объектами сеанса, а не для полной реализации logout().

Установка тестовых cookie

Для удобства Django предоставляет способ проверки, принимает ли браузер пользователя куки. Вызовите метод set_test_cookie() объекта request.session в представлении, а затем вызовите test_cookie_worked() в последующем представлении — не в том же вызове.

Это неудобное разделение между set_test_cookie() и test_cookie_worked() необходимо из-за работы с куки. Когда вы устанавливаете куки, вы не можете узнать, принял ли браузер куки, пока браузер не отправит следующий запрос.

Хорошей практикой является использование delete_test_cookie() для очистки после себя. Сделайте это после того, как вы убедились, что тестовый куки сработал.

Вот типичный пример использования:

from django.http import HttpResponse
from django.shortcuts import render


def login(request):
    if request.method == "POST":
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            return HttpResponse("You're logged in.")
        else:
            return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render(request, "foo/login_form.html")
Изменено в Django 5.1:

Добавлена поддержка установки тестовых куки в асинхронных функциях представлений.

Использование сессий вне представлений

Примечание

Примеры в этом разделе импортируют объект SessionStore напрямую из бэкенда django.contrib.sessions.backends.db. В своём коде вы должны импортировать SessionStore из движка сессий, указанного в SESSION_ENGINE, как показано ниже:

>>> from importlib import import_module
>>> from django.conf import settings
>>> SessionStore = import_module(settings.SESSION_ENGINE).SessionStore

Доступен API для управления данными сессий вне представления:

>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s["last_login"] = 1376587691
>>> s.create()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key="2b1189a188b44ad18c35e113ac6ceead")
>>> s["last_login"]
1376587691

SessionStore.create() предназначен для создания новой сессии (т.е. сессии, не загруженной из хранилища сессий и с session_key=None). save() предназначен для сохранения существующей сессии (т.е. сессии, загруженной из хранилища сессий). Вызов save() для новой сессии также может сработать, но есть небольшой шанс создания session_key, который конфликтует с существующей. create() вызывает save() и циклически повторяет его, пока не сгенерируется session_key, который не используется.

Если вы используете бэкенд django.contrib.sessions.backends.db, каждая сессия — обычная модель Django. Модель Session определена в django/contrib/sessions/models.py. Поскольку это обычная модель, вы можете получить доступ к сессиям с помощью обычного API базы данных Django:

>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk="2b1189a188b44ad18c35e113ac6ceead")
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)

Обратите внимание, что вам нужно вызвать get_decoded(), чтобы получить словарь сессии. Это необходимо, так как словарь хранится в закодированном формате:

>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}

Когда сессии сохраняются

По умолчанию Django сохраняет данные в базе данных сессий только при изменении сессии — если любое значение в её словаре было присвоено или удалено:

# Session is modified.
request.session["foo"] = "bar"

# Session is modified.
del request.session["foo"]

# Session is modified.
request.session["foo"] = {}

# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session["foo"]["bar"] = "baz"

В последнем случае из примера выше мы можем явно указать объекту сессии, что она была изменена, установив атрибут modified на объекте сессии:

request.session.modified = True

Чтобы изменить это поведение по умолчанию, установите настройку SESSION_SAVE_EVERY_REQUEST в True. Когда она установлена в True, Django будет сохранять сессию в базе данных на каждом запросе.

Обратите внимание, что куки сессии отправляются только при создании или изменении сессии. Если SESSION_SAVE_EVERY_REQUEST равна True, куки сессии будут отправляться на каждом запросе.

Аналогично, часть expires куки сессии обновляется каждый раз при отправке куки сессии.

Сессия не сохраняется, если код состояния ответа равен 500.

Сессии на время работы браузера против постоянных сессий

Вы можете управлять тем, использует ли механизм сессий сессии на время работы браузера или постоянные сессии, с помощью настройки SESSION_EXPIRE_AT_BROWSER_CLOSE.

По умолчанию SESSION_EXPIRE_AT_BROWSER_CLOSE установлена в False, что означает, что куки сессии будут храниться в браузерах пользователей до тех пор, пока действует SESSION_COOKIE_AGE. Используйте эту настройку, если не хотите, чтобы пользователи каждый раз при открытии браузера должны были входить в систему.

Если SESSION_EXPIRE_AT_BROWSER_CLOSE установлена в True, Django будет использовать куки на время работы браузера — куки, которые истекают, как только пользователь закрывает свой браузер. Используйте эту настройку, если хотите, чтобы пользователи каждый раз при открытии браузера должны были входить в систему.

Эта настройка является глобальным значением по умолчанию и может быть переопределена на уровне каждой сессии путём явного вызова метода set_expiry() объекта request.session, как описано выше в разделе использование сессий в представлениях.

Примечание

Некоторые браузеры (например, Chrome) предоставляют настройки, позволяющие пользователям продолжить сеансы браузера после закрытия и повторного открытия браузера. В некоторых случаях это может влиять на настройку SESSION_EXPIRE_AT_BROWSER_CLOSE и препятствовать истечению сессий при закрытии браузера. Учтите это, тестируя приложения Django, в которых включена настройка SESSION_EXPIRE_AT_BROWSER_CLOSE.

Очистка хранилища сессий

По мере создания новых сессий на вашем веб-сайте данные сессий накапливаются в хранилище сессий. Если вы используете бэкенд базы данных, таблица базы данных django_session будет увеличиваться. Если вы используете бэкенд файлов, в вашей временной директории будет увеличиваться количество файлов.

Чтобы понять эту проблему, рассмотрим, что происходит с бэкендом базы данных. Когда пользователь входит в систему, Django добавляет строку в таблицу базы данных django_session. Django обновляет эту строку каждый раз, когда данные сессии изменяются. Если пользователь выходит из системы вручную, Django удаляет строку. Но если пользователь не выходит из системы, строка никогда не удаляется. Аналогичный процесс происходит с бэкендом файлов.

Django не предоставляет автоматическое удаление истекших сессий. Поэтому вам необходимо регулярно удалять истекшие сессии. Для этого Django предоставляет команду управления очисткой: clearsessions. Рекомендуется вызывать эту команду регулярно, например, как ежедневную задачу cron.

Обратите внимание, что бэкенд кеша не уязвим к этой проблеме, потому что кеши автоматически удаляют устаревшие данные. То же относится и к бэкенду куки, потому что данные сессии хранятся в браузерах пользователей.

Настройки

Несколько настроек Django позволяют управлять поведением сессий:

  • SESSION_CACHE_ALIAS
  • SESSION_COOKIE_AGE
  • SESSION_COOKIE_DOMAIN
  • SESSION_COOKIE_HTTPONLY
  • SESSION_COOKIE_NAME
  • SESSION_COOKIE_PATH
  • SESSION_COOKIE_SAMESITE
  • SESSION_COOKIE_SECURE
  • SESSION_ENGINE
  • SESSION_EXPIRE_AT_BROWSER_CLOSE
  • SESSION_FILE_PATH
  • SESSION_SAVE_EVERY_REQUEST
  • SESSION_SERIALIZER

Безопасность сессий

Поддомены внутри сайта могут устанавливать куки на клиенте для всего домена. Это делает фиксацию сессии возможной, если куки разрешены с поддоменов, не контролируемых доверенными пользователями.

Например, злоумышленник может войти в good.example.com и получить действительную сессию для своей учётной записи. Если злоумышленник контролирует bad.example.com, он может использовать его для отправки своего ключа сессии вам, так как поддомен может устанавливать куки на *.example.com. Когда вы посетите good.example.com, вы войдёте в систему как злоумышленник и можете случайно ввести свои конфиденциальные данные (например, данные кредитной карты) в учётную запись злоумышленника.

Другой возможной атакой будет то, что good.example.com установит своё SESSION_COOKIE_DOMAIN на "example.com", что приведет к отправке куки сессий с этого сайта на bad.example.com.

Технические детали

  • Словарь сессий принимает любые сериализуемые значения json при использовании JSONSerializer.
  • Данные сессии хранятся в таблице базы данных с именем django_session .
  • Django отправляет куки только в том случае, если это необходимо. Если вы не устанавливаете данные сессии, он не отправляет куки сессии.

Объект SessionStore

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

Все SessionStore подклассы, доступные в Django, реализуют следующие методы манипуляции данными:

  • exists()
  • create()
  • save()
  • delete()
  • load()
  • clear_expired()

Асинхронный интерфейс для этих методов предоставляется путем обертывания их с помощью sync_to_async(). Они могут быть реализованы непосредственно, если доступна асинхронная реализация:

  • aexists()
  • acreate()
  • asave()
  • adelete()
  • aload()
  • aclear_expired()

Для создания пользовательского движка сессий или настройки существующего можно создать новый класс, унаследованный от SessionBase или любого другого существующего SessionStore класса.

Можно расширять движки сессий, но для работы с движками сессий, поддерживаемыми базой данных, обычно требуется дополнительная работа (см. следующий раздел для получения подробностей).

Изменено в Django 5.1:

aexists(), acreate(), asave(), adelete(), aload(), и aclear_expired() методы были добавлены.

Расширение движков сессий, поддерживаемых базой данных

Для создания пользовательского движка сессий, поддерживаемого базой данных, на основе тех, что включены в Django (а именно db и cached_db), можно унаследовать от AbstractBaseSession и либо SessionStore класса.

AbstractBaseSession и BaseSessionManager импортируются из django.contrib.sessions.base_session, чтобы их можно было импортировать без включения django.contrib.sessions в INSTALLED_APPS.

class base_session.AbstractBaseSession

Абстрактная базовая модель сессии.

session_key

Основной ключ. Само поле может содержать до 40 символов. Текущая реализация генерирует строку длиной 32 символа (случайная последовательность цифр и строчных букв ASCII).

session_data

Строка, содержащая закодированный и сериализованный словарь сессии.

expire_date

Дата и время истечения срока действия сессии.

Истекшие сессии недоступны для пользователя, однако они всё ещё могут храниться в базе данных до тех пор, пока не будет выполнена команда управления clearsessions.

classmethod get_session_store_class()

Возвращает класс хранилища сессий, который будет использоваться с этой моделью сессии.

get_decoded()

Возвращает декодированные данные сессии.

Декодирование выполняется классом хранилища сессий.

Вы также можете настроить менеджер модели, унаследовав от BaseSessionManager:

class base_session.BaseSessionManager
encode(session_dict)

Возвращает заданный словарь сессии, сериализованный и закодированный в виде строки.

Кодирование выполняется связанным с классом модели классом хранилища сессий.

save(session_key, session_dict, expire_date)

Сохраняет данные сессии для заданного ключа сессии или удаляет сессию, если данные пусты.

Настройка SessionStore классов достигается путем переопределения методов и свойств, описанных ниже:

class backends.db.SessionStore

Реализует хранилище сессий, поддерживаемое базой данных.

classmethod get_model_class()

Переопределите этот метод, чтобы вернуть пользовательскую модель сессии, если она вам нужна.

create_model_instance(data)

Возвращает новый экземпляр объекта модели сессии, который представляет текущее состояние сессии.

Переопределение этого метода предоставляет возможность изменить данные модели сессии перед сохранением в базе данных.

class backends.cached_db.SessionStore

Реализует кэшированное хранилище сессий, поддерживаемое базой данных.

cache_key_prefix

Префикс, добавляемый к ключу сессии для построения строки ключа кэша.

Пример

Пример ниже демонстрирует пользовательский движок сессий, поддерживаемый базой данных, который включает дополнительный столбец базы данных для хранения идентификатора учетной записи (что обеспечивает возможность запроса базы данных для всех активных сессий для учетной записи):

from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models


class CustomSession(AbstractBaseSession):
    account_id = models.IntegerField(null=True, db_index=True)

    @classmethod
    def get_session_store_class(cls):
        return SessionStore


class SessionStore(DBStore):
    @classmethod
    def get_model_class(cls):
        return CustomSession

    def create_model_instance(self, data):
        obj = super().create_model_instance(data)
        try:
            account_id = int(data.get("_auth_user_id"))
        except (ValueError, TypeError):
            account_id = None
        obj.account_id = account_id
        return obj

Если вы мигрируете из встроенного cached_db хранилища сессий Django в пользовательское, основанное на cached_db, вы должны переопределить префикс ключа кэша, чтобы избежать конфликта имен:

class SessionStore(CachedDBStore):
    cache_key_prefix = "mysessions.custom_cached_db_backend"

    # ...

Идентификаторы сессий в URL-адресах

Фреймворк сессий Django полностью и исключительно основан на cookie. Он не обращается к добавлению идентификаторов сессий в URL-адреса в качестве последнего средства, как это делает PHP. Это преднамеренное дизайнерское решение. Такое поведение не только ухудшает внешний вид URL-адресов, но и делает ваш сайт уязвимым к краже идентификаторов сессии через заголовок «Referer».

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.1/topics/http/sessions/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API