Skip to main content

The Default Secure Cookie Implementation

When you attempt to store data in flask.session without configuring a SECRET_KEY, Flask raises a RuntimeError via the NullSession class. This is because Flask's default session implementation, SecureCookieSessionInterface, requires a cryptographic key to sign session data, ensuring that users cannot tamper with their session state.

The Session Object​

The SecureCookieSession class is the default object used for flask.session. It behaves like a standard Python dictionary but includes logic to track whether it has been changed during a request.

from flask import session

@app.route("/set")
def set_session():
session["user_id"] = 42 # Automatically sets session.modified = True
return "Session set"

Internally, SecureCookieSession inherits from werkzeug.datastructures.CallbackDict. It uses an on_update callback to set its modified attribute to True whenever a key is set or deleted.

The Nested Object Gotcha​

Because Flask only tracks top-level changes to the session dictionary, modifying a mutable object stored inside the session will not automatically trigger a save.

@app.route("/update_prefs")
def update_prefs():
# This modification is NOT detected by CallbackDict
session["prefs"]["theme"] = "dark"

# You must manually flag the session as modified
session.modified = True
return "Preferences updated"

Security through Signing​

Flask uses the itsdangerous library to sign session cookies. The SecureCookieSessionInterface.get_signing_serializer method creates a URLSafeTimedSerializer using your application's SECRET_KEY.

Important: Session cookies in Flask are signed, not encrypted. This means:

  1. Integrity: Users cannot modify the session data (e.g., changing user_id from 42 to 1) because the cryptographic signature would become invalid.
  2. Visibility: Users can read the contents of the session cookie by decoding the base64-encoded payload. Never store sensitive information like passwords or API keys in the session.

Key Rotation with Fallbacks​

If you need to change your SECRET_KEY without invalidating all existing user sessions, you can use SECRET_KEY_FALLBACKS. When SecureCookieSessionInterface.open_session attempts to load a cookie, it uses the get_signing_serializer to check the current key and then any fallback keys provided in the configuration.

app.config.update(
SECRET_KEY="new-very-secret-key",
SECRET_KEY_FALLBACKS=["old-secret-key", "even-older-key"]
)

In SecureCookieSessionInterface.get_signing_serializer, Flask ensures the current secret_key is at the top of the list because itsdangerous uses the first key for signing new cookies while using the rest for verifying existing ones.

Serialization of Complex Types​

Flask uses a specialized TaggedJSONSerializer (aliased as session_json_serializer in flask.sessions) to convert session data to a string. Unlike standard JSON, this serializer supports several Python-specific types:

  • datetime objects
  • UUID objects
  • tuple (distinguished from lists)
  • bytes
  • Markup strings (from MarkupSafe)

This allows you to store objects like a timezone-aware datetime directly in the session:

from datetime import datetime, timezone

@app.route("/stamp")
def stamp():
session["last_login"] = datetime.now(timezone.utc)
return "Timestamped"

Session Lifecycle​

The SecureCookieSessionInterface manages the session through two primary methods:

  1. open_session(app, request): Called at the start of a request. It retrieves the cookie named by SESSION_COOKIE_NAME, verifies the signature using itsdangerous, and returns a SecureCookieSession object. If the signature is invalid (e.g., the key changed or the cookie was tampered with), it returns a fresh, empty session.
  2. save_session(app, session, response): Called at the end of a request. It checks session.should_set_cookie (which looks at session.modified). If modified, it serializes the data, signs it, and adds a Set-Cookie header to the response.

Configuration Reference​

You can customize the behavior of the session cookie using the following configuration variables in your Flask app:

Config KeyDefaultDescription
SESSION_COOKIE_NAME"session"The name of the cookie stored on the client.
SESSION_COOKIE_HTTPONLYTruePrevents JavaScript from accessing the cookie.
SESSION_COOKIE_SECUREFalseIf True, the cookie is only sent over HTTPS.
SESSION_COOKIE_SAMESITE"Lax"Controls CSRF protection via the SameSite attribute.
PERMANENT_SESSION_LIFETIME31 daysHow long a "permanent" session lasts.

To make a session persist after the browser closes, set session.permanent = True. This causes get_expiration_time to return a timestamp based on PERMANENT_SESSION_LIFETIME instead of creating a "session-only" cookie.