Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Managing HTTP State in Django: Cookies and Sessions Explained

Tech 1

The Role of Cookies in Stateless HTTP

HTTP functions as a stateless protocol, with each request being isolated and independent of previous interactions. This design creates a challenge when applications need to preserve user data across multiple page loads. Cookies serve as client-side key-value stores carried within the browser, solving this problem.

A server generates a cookie and includes it in the response headers. The browser then stores this data locally and attaches it to subsequent requests, allowing the server to reconstruct session context.

Working with Cookies in Djengo

Storing Values on the Client

Django provides several methods on the response object to set cookies. The standard approach uses set_cookie, while set_signed_cookie adds a cryptographic signature to verify integrity.

# Typical usage on a response object
resp = render(request, 'template.html')
# or resp = HttpResponse(...)

resp.set_cookie('preference', 'dark_mode')
resp.set_signed_cookie('cart_id', 'abc123', salt='hash-salt-here')

return resp

The following parameters control cookie behavior:

  • max_age: Duration in seconds; None persists until the browser closes.
  • expires: A datetime string or object, often needed for Internet Explorer compatibility.
  • path: URL prefix where the cookie is valid (e.g., /store/).
  • domain: Enables cross-subdomain sharing, such as .mysite.com.
  • secure: Transmit only over HTTPS.
  • httponly: Block JavaScript access, though not foolproof against packet inspection.

Signed cookies work by internally calling set_cookie:

def set_signed_cookie(self, key, value, salt='', **kwargs):
    value = signing.get_cookie_signer(salt=key + salt).sign(value)
    return self.set_cookie(key, value, **kwargs)

To retrieve signed values later:

raw_value = request.get_signed_cookie('cart_id', salt='hash-salt-here', default=None)

Accessing and Removing Cookies

On incoming requests, cookies are available through a dictionary-like interface:

# Direct lookup or using .get for safe access
selected = request.COOKIES.get('preference')
status = request.COOKIES.get('login_flag', 'false')

To delete a cookie, instruct the browser to expire it via the response:

resp.delete_cookie('preference')

Practical Login Guard with Cookies

A decorator can enforce authentication by checking for a signed cookie before allowing access to protected views.

from functools import wraps
from django.shortcuts import redirect, render

def require_auth(func):
    @wraps(func)
    def wrapper(request, *args, **kwargs):
        target = request.get_full_path()
        if request.get_signed_cookie('auth_token', salt='secret-2025', default=None) == 'valid':
            return func(request, *args, **kwargs)
        return redirect(f'/login/?next={target}')
    return wrapper

def login_view(request):
    if request.get_signed_cookie('auth_token', salt='secret-2025', default=None) == 'valid':
        return HttpResponse('Already logged in')
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'admin' and password == 'secretpass':
            redirect_to = request.GET.get('next', '/dashboard/')
            reply = redirect(redirect_to)
            reply.set_signed_cookie('auth_token', 'valid', salt='secret-2025')
            return reply
    return render(request, 'login.html')

@require_auth
def dashboard(request):
    return HttpResponse('secure content')

Server-Side Persistence with Sessions

Cookies help track state but store data on the client, which introduces security risks. Sessions shift storage to the server. Django creates a unique session identifier, sends it to the browser as a cookie (typically named sessionid), and maintains session data in a backend.

Before using sessions, run python manage.py migrate to create the necessary database table.

Core Session Operations

Interacting with the session resembles working with a dictionary on the request object:

# Setting values triggers the creation of a session entry and cookie
request.session['theme'] = 'ocean'
request.session.setdefault('visits', 0)

# Retrieving data
name = request.session.get('user_name', 'Guest')

# Removing a specific key
del request.session['temporary_data']

# Iterating over keys, values, or pairs
for k in request.session.keys():
    pass
for v in request.session.values():
    pass
for k, v in request.session.items():
    pass

# Getting the auto-generated session identifier
session_key = request.session.session_key

Several utility methods manage session lifecycle:

  • request.session.clear_expired() removes stale entries from the database.
  • request.session.exists('some_key') verifies a session key exists.
  • request.session.delete() removes only the database row.
  • request.session.flush() removes both the database row and the client cookie—useful during logout.

Sesssion expiry is configurable:

# Expire after 1800 seconds (30 minutes)
request.session.set_expiry(1800)
# Expire on browser close
request.session.set_expiry(0)
# Use the global policy from settings
request.session.set_expiry(None)

Available Session Backends

Django's SESSION_ENGINE setting determines where data lives:

  • Database (default): django.contrib.sessions.backends.db
  • Caching system: django.contrib.sessions.backends.cache (requires SESSION_CACHE_ALIAS)
  • File system: django.contrib.sessions.backends.file (configure SESSION_FILE_PATH)
  • Hybrid cache and database: django.contrib.sessions.backends.cached_db
  • Signed cookies: django.contrib.sessions.backends.signed_cookies (data resides on client but is signed)

Common configuration variables in settings.py include:

SESSION_COOKIE_NAME = 'sessionid'
SESSION_COOKIE_AGE = 1209600  # two weeks in seconds
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_SAVE_EVERY_REQUEST = False

Implementing Login Checks Using Sessions

The decorator pattern adjusts naturally to session-based authentication:

def restrict_access(func):
    @wraps(func)
    def wrapper(request, *args, **kwargs):
        next_page = request.get_full_path()
        if request.session.get('authenticated') == 'yes':
            return func(request, *args, **kwargs)
        return redirect(f'/login/?next={next_page}')
    return wrapper

def login(request):
    if request.session.get('authenticated') == 'yes':
        return HttpResponse('You are already authenticated')
    if request.method == 'POST':
        user = request.POST.get('username')
        passw = request.POST.get('password')
        if user == 'admin' and passw == 'p@ss':
            request.session['authenticated'] = 'yes'
            destination = request.GET.get('next', '/dashboard/')
            return redirect(destination)
    return render(request, 'login_form.html')

Using the Decorator on Class-Based Views

Class-based views need a different approach for applying function decorators, typically via method_decorator.

Decorating Individual HTTP Methods

from django.utils.decorators import method_decorator

class ProfileView(View):
    def get(self, request):
        return render(request, 'profile.html')
    
    @method_decorator(restrict_access)
    def post(self, request):
        # Handle form submission
        return redirect('/dashboard/')

Wrapping the dispatch Method

Decorating dispatch secures all handlers (get, post, etc.) at once.

class SettingsView(View):
    @method_decorator(restrict_access)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, 'settings.html')

Applying at the Class Level

When decorating the class directly, specify the method name with the name parameter.

@method_decorator(restrict_access, name='get')
@method_decorator(restrict_access, name='post')
class ReportsView(View):
    def get(self, request):
        return render(request, 'reports.html')
    
    def post(self, request):
        # Generate report
        return redirect('/reports/')

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.