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:
- Integrity: Users cannot modify the session data (e.g., changing
user_idfrom42to1) because the cryptographic signature would become invalid. - 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:
datetimeobjectsUUIDobjectstuple(distinguished from lists)bytesMarkupstrings (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:
open_session(app, request): Called at the start of a request. It retrieves the cookie named bySESSION_COOKIE_NAME, verifies the signature usingitsdangerous, and returns aSecureCookieSessionobject. If the signature is invalid (e.g., the key changed or the cookie was tampered with), it returns a fresh, empty session.save_session(app, session, response): Called at the end of a request. It checkssession.should_set_cookie(which looks atsession.modified). If modified, it serializes the data, signs it, and adds aSet-Cookieheader to the response.
Configuration Reference
You can customize the behavior of the session cookie using the following configuration variables in your Flask app:
| Config Key | Default | Description |
|---|---|---|
SESSION_COOKIE_NAME | "session" | The name of the cookie stored on the client. |
SESSION_COOKIE_HTTPONLY | True | Prevents JavaScript from accessing the cookie. |
SESSION_COOKIE_SECURE | False | If True, the cookie is only sent over HTTPS. |
SESSION_COOKIE_SAMESITE | "Lax" | Controls CSRF protection via the SameSite attribute. |
PERMANENT_SESSION_LIFETIME | 31 days | How 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.