Skip to main content

Session Management and Cookie Security

Session management in this codebase is handled by a combination of middleware and pluggable storage backends. The system abstracts the complexity of persisting data across HTTP requests, providing a dictionary-like interface via request.session.

The Session Lifecycle

The core of session management is SessionMiddleware (found in django/contrib/sessions/middleware.py). It manages the transition of session data between the client's cookie and the server's storage.

Request Processing

When a request enters the system, SessionMiddleware.process_request retrieves the session key from the client's cookies using the SESSION_COOKIE_NAME setting. It then instantiates the appropriate SessionStore engine:

def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)

Response Processing

In process_response, the middleware determines if the session needs to be saved or deleted. A session is saved if it has been modified (e.g., a value was set) or if SESSION_SAVE_EVERY_REQUEST is enabled.

If the session is empty and a session cookie exists, the middleware deletes the cookie from the client. Otherwise, it saves the session data and sends a refreshed cookie back to the client with security flags like httponly and secure.

Storage Backends

The codebase provides several implementations of SessionStore, all inheriting from the abstract SessionBase in django/contrib/sessions/backends/base.py.

Database Backend (db)

The default engine (django.contrib.sessions.backends.db) persists session data in the django_session table. It uses the Session model from django/contrib/sessions/models.py.

  • Persistence: High. Data survives server restarts.
  • Performance: Moderate. Requires a database query for every session load.

Cache Backend (cache)

The cache backend (django.contrib.sessions.backends.cache) stores data in the configured Django cache (e.g., Memcached or Redis).

  • Persistence: Low. Data may be lost if the cache is cleared or keys are evicted.
  • Performance: High. Extremely fast reads and writes.

Cached Database Backend (cached_db)

The cached_db backend (django.contrib.sessions.backends.cached_db) provides a write-through cache. It reads from the cache first and falls back to the database, ensuring both high performance and persistence.

The signed_cookies backend (django.contrib.sessions.backends.signed_cookies) stores the entire session dictionary directly in the client's cookie. It uses django.core.signing to ensure the data cannot be tampered with.

  • Persistence: High (stored on client).
  • Performance: High (no server-side storage lookup).
  • Constraint: Limited to 4KB total size due to browser cookie limits.

Session Security

Security is integrated into the session lifecycle through cryptographic signing and secure cookie configuration.

SessionMiddleware applies several security flags to the session cookie based on project settings:

  • HttpOnly: Controlled by SESSION_COOKIE_HTTPONLY (default True). Prevents client-side scripts from accessing the session cookie, mitigating XSS attacks.
  • Secure: Controlled by SESSION_COOKIE_SECURE. If True, the cookie is only sent over HTTPS.
  • SameSite: Controlled by SESSION_COOKIE_SAMESITE (default 'Lax'). Helps prevent CSRF attacks.

Session Fixation Protection

To prevent session fixation attacks, the codebase provides cycle_key(). This method creates a new session ID while retaining the existing session data. This is used extensively in django/contrib/auth/__init__.py during the login process:

# From django/contrib/auth/__init__.py
if SESSION_KEY in request.session:
# ... logic to check if user changed ...
request.session.flush()
else:
request.session.cycle_key()

Data Integrity

All session data is serialized and signed. SessionBase.encode uses django.core.signing.dumps with a salt derived from the class name to ensure that even if a backend is compromised, the session data remains protected against tampering.

Expiry and Persistence

Sessions can be configured to expire at a specific time or when the user closes their browser.

Expiry Logic

SessionBase provides set_expiry(value), where value can be:

  • An integer: Seconds of inactivity before expiry.
  • A datetime/timedelta: A specific point in time for expiry.
  • 0: The session expires when the browser is closed.
  • None: Uses the global SESSION_COOKIE_AGE setting.

The middleware checks get_expire_at_browser_close() to decide whether to set a max_age and expires attribute on the response cookie.

Clearing Expired Sessions

For backends that don't automatically expire data (like db or file), the clear_expired() class method is provided. For example, the database backend filters by expire_date:

# From django/contrib/sessions/backends/db.py
@classmethod
def clear_expired(cls):
cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete()