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.
Signed Cookie Backend (signed_cookies)
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.
Cookie Protection
SessionMiddleware applies several security flags to the session cookie based on project settings:
- HttpOnly: Controlled by
SESSION_COOKIE_HTTPONLY(defaultTrue). Prevents client-side scripts from accessing the session cookie, mitigating XSS attacks. - Secure: Controlled by
SESSION_COOKIE_SECURE. IfTrue, 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_AGEsetting.
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()