Skip to main content

Django Secure Cookies

Introduction

Cookies are small pieces of data stored on a user's browser by websites they visit. In web applications, they're essential for maintaining state between HTTP requests, storing session information, and remembering user preferences. However, improperly implemented cookies can become an attack vector for malicious actors.

Django, a high-level Python web framework, provides several built-in features for implementing secure cookies. In this tutorial, we'll explore how Django handles cookies securely and how you can leverage these features in your applications.

Understanding Cookies in Web Security

Before diving into Django's implementation, let's understand why secure cookies matter:

  1. Authentication: Cookies often store session tokens that identify logged-in users
  2. Privacy: They may contain personal information or preferences
  3. Cross-site vulnerabilities: Insecure cookies can enable CSRF (Cross-Site Request Forgery) attacks
  4. Data theft: Without proper protections, cookies can be stolen or tampered with

Django handles cookies securely through several key settings and features:

1. Session Cookies

Django uses cookies to store session data by default. The framework applies several security measures automatically:

python
# In settings.py - These are default settings you can customize
SESSION_COOKIE_SECURE = True # Ensures cookies are only sent over HTTPS
SESSION_COOKIE_HTTPONLY = True # Prevents JavaScript access to cookies
SESSION_COOKIE_SAMESITE = 'Lax' # Controls cross-site cookie behavior

These settings should be added to your settings.py file. Let's explore what each one does:

  • SESSION_COOKIE_SECURE: When set to True, cookies will only be sent over HTTPS connections, preventing them from being intercepted over insecure networks
  • SESSION_COOKIE_HTTPONLY: This setting prevents JavaScript from accessing the cookie, helping protect against XSS (Cross-Site Scripting) attacks
  • SESSION_COOKIE_SAMESITE: Controls how cookies are sent in cross-site requests, with options like 'Strict', 'Lax', or 'None'

2. CSRF Protection

Django's built-in CSRF (Cross-Site Request Forgery) protection relies on secure cookies. When you include the CSRF token in your forms, Django creates a cookie that helps verify request authenticity:

python
# settings.py
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = 'Lax'

In your templates, you'll include the CSRF token like this:

html
<form method="post">
{% csrf_token %}
<!-- form fields -->
<button type="submit">Submit</button>
</form>

3. Setting and Reading Cookies in Django Views

While Django handles session and CSRF cookies automatically, sometimes you need to set custom cookies for your application logic:

python
from django.http import HttpResponse

def set_cookie_example(request):
response = HttpResponse("Cookie has been set!")
# Set a secure cookie
response.set_cookie(
'user_preference',
'dark_mode',
secure=True, # HTTPS only
httponly=True, # No JavaScript access
samesite='Lax', # SameSite policy
max_age=604800 # 1 week in seconds
)
return response

To read cookies in a view:

python
def read_cookie_example(request):
user_pref = request.COOKIES.get('user_preference', 'light_mode')
return HttpResponse(f"Your preference is: {user_pref}")

Best Practices for Secure Cookies in Django

1. Use HTTPS Everywhere

For secure cookies to work properly, your site must use HTTPS. In production:

python
# settings.py for production
SECURE_SSL_REDIRECT = True # Redirects all HTTP traffic to HTTPS
SECURE_HSTS_SECONDS = 31536000 # 1 year, enables HSTS
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

For sensitive operations, use shorter-lived cookies:

python
# Example setting a short-lived cookie
response.set_cookie(
'temporary_action',
'value',
max_age=300, # 5 minutes
secure=True,
httponly=True
)

3. Use Django's Built-in Session Framework

Rather than managing cookies directly, use Django's session framework when possible:

python
# Store data in the session
def save_to_session(request):
request.session['user_theme'] = 'dark'
request.session['last_action'] = 'profile_update'
return HttpResponse("Session data saved")

# Read data from the session
def read_from_session(request):
theme = request.session.get('user_theme', 'light')
return HttpResponse(f"Your theme is: {theme}")

Real-World Example: Remember Me Functionality

Let's implement a "Remember Me" checkbox for a login form:

python
from django.contrib.auth import login
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm

def login_view(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
user = form.get_user()
remember_me = request.POST.get('remember_me') == 'on'

# Log the user in
login(request, user)

# Set session expiration based on remember me checkbox
if remember_me:
# Session will last for 2 weeks (14 days)
request.session.set_expiry(1209600)
else:
# Session expires when browser closes
request.session.set_expiry(0)

return redirect('home')
else:
form = AuthenticationForm()

return render(request, 'login.html', {'form': form})

And in your template:

html
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<div class="form-check">
<input type="checkbox" name="remember_me" id="remember_me">
<label for="remember_me">Remember Me</label>
</div>
<button type="submit">Login</button>
</form>

It's important to verify that your cookies are secure. Django's test client can help:

python
from django.test import TestCase, Client

class CookieSecurityTest(TestCase):
def test_session_cookie_attributes(self):
client = Client()
response = client.get('/')

# Check if the session cookie exists
self.assertIn('sessionid', response.cookies)

# Verify cookie attributes
session_cookie = response.cookies['sessionid']
self.assertTrue(session_cookie['secure'])
self.assertTrue(session_cookie['httponly'])

Additionally, you can use browser developer tools to inspect cookies and their attributes in a live environment.

1. Session Hijacking

If your cookies aren't secure, they can be stolen through network eavesdropping. Always use secure=True and deploy on HTTPS.

2. Session Fixation

This occurs when an attacker sets a user's session ID, then waits for the user to authenticate. Django mitigates this by regenerating session IDs during login:

python
# This is done automatically by Django's login() function
# But you can manually rotate session keys too:
from django.contrib.sessions.backends.base import UpdateError

def rotate_session_key(request):
while True:
try:
request.session.create()
break
except UpdateError:
# A new session key was generated but not saved
continue

3. CSRF Attacks

Always use Django's CSRF protection for any view that modifies server state:

python
from django.views.decorators.csrf import csrf_protect

@csrf_protect
def update_profile(request):
# Process form data
pass

Summary

Secure cookies are essential for maintaining a safe Django application. By leveraging Django's built-in security features for cookies, you can protect your users from common web vulnerabilities.

Key takeaways:

  1. Always set SESSION_COOKIE_SECURE, SESSION_COOKIE_HTTPONLY, and CSRF_COOKIE_SECURE to True
  2. Use HTTPS for all traffic in production
  3. Leverage Django's session framework rather than handling cookies directly
  4. Set appropriate cookie expiration times based on sensitivity
  5. Test your cookie security configurations

Additional Resources

Exercises

  1. Configure your Django application to use secure cookies with all recommended settings in settings.py
  2. Create a view that sets a custom secure cookie and another view that reads it
  3. Implement a "Remember Me" functionality in your authentication system
  4. Write a test case to verify that your cookies have the correct security attributes
  5. Use browser developer tools to inspect cookies on your website and verify their security attributes


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)