Typorithm · easy

Flask: signed sessions, tampering and secret rotation

pythonflaskindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Flask: signed sessions, tampering and secret rotation

0%

Concept and contract

Understand that default cookie sessions are signed, not encrypted.

Flask’s default session stores serialized data in a client cookie protected by a signature. Signing detects unauthorized modification; it does not hide the readable content from the client.

Store minimal non-secret session data and keep the signing key outside public source in production. The hard-coded key in this example is explicitly confined to an owned local learning fixture.

Mechanism and worked example

The helper creates a fresh app and client, stores a theme, then reads it through another request.

The helper creates a fresh app and client, stores a theme, then reads it through another request. An optional tamper step corrupts the signature-bearing cookie.

Flask then treats the untrusted session as absent, returning the declared default. This demonstrates integrity and client persistence without implying a server-side session database or encryption of the stored value.

Complete worked example

python

from flask import Flask, session

def session_roundtrip(value, tamper=False):
    app = Flask(__name__)
    app.config.update(TESTING=True, SECRET_KEY='owned-course-key')
    @app.get('/set')
    def store():
        session['theme'] = value
        return {'saved': True}
    @app.get('/get')
    def read():
        return {'theme': session.get('theme', 'default')}
    client = app.test_client()
    client.get('/set')
    if tamper:
        cookie = client.get_cookie('session').value
        client.set_cookie('session', cookie + 'broken')
    response = client.get('/get')
    return response.json
Reasoning
The helper creates a fresh app and client, stores a theme, then reads it through another request. An optional tamper step corrupts the signature-bearing cookie.

Counterexamples and common mistakes

The counterexample replaces the observed session result with the original input, so a corrupted cookie appears to be accepted.

The counterexample replaces the observed session result with the original input, so a corrupted cookie appears to be accepted. Always inspect the actual request-visible session.

Avoid storing passwords, tokens or private documents inside a signed cookie. Secure, HttpOnly and SameSite settings address transport and browser exposure policies; none turns a readable signed payload into an encrypted secret container.

Counterexample: a runnable framework mistake

python

from flask import Flask, session

def session_roundtrip(value, tamper=False):
    app = Flask(__name__)
    app.config.update(TESTING=True, SECRET_KEY='owned-course-key')
    @app.get('/set')
    def store():
        session['theme'] = value
        return {'saved': True}
    @app.get('/get')
    def read():
        return {'theme': session.get('theme', 'default')}
    client = app.test_client()
    client.get('/set')
    if tamper:
        cookie = client.get_cookie('session').value
        client.set_cookie('session', cookie + 'broken')
    response = client.get('/get')
    return {'theme': value}
Reasoning
The counterexample replaces the observed session result with the original input, so a corrupted cookie appears to be accepted. Always inspect the actual request-visible session.

Independent practice: one bounded framework change

Extend the helper with a rotate flag.

Extend the helper with a rotate flag. After storing the cookie, change SECRET_KEY to a new fixture key and put the old key in SECRET_KEY_FALLBACKS.

Current Flask supports that bounded key-rotation mechanism. An intact old cookie should remain readable, while a corrupted cookie must still fail. Keep the fallback list explicitly owned and short rather than accepting arbitrary request-provided keys.

Step 1: Rotate the signing key with an explicit old-key fallback. Step 2: Add rotate as an optional helper argument.

Practice starter: solve before reading the solution

python

from flask import Flask, session

def session_roundtrip(value, tamper=False):
    # Remove this placeholder, then adapt the retained baseline:
    # Rotate the signing key with an explicit old-key fallback.
    raise NotImplementedError
    app = Flask(__name__)
    app.config.update(TESTING=True, SECRET_KEY='owned-course-key')
    @app.get('/set')
    def store():
        session['theme'] = value
        return {'saved': True}
    @app.get('/get')
    def read():
        return {'theme': session.get('theme', 'default')}
    client = app.test_client()
    client.get('/set')
    if tamper:
        cookie = client.get_cookie('session').value
        client.set_cookie('session', cookie + 'broken')
    response = client.get('/get')
    return response.json
Reasoning
Rotate the signing key with an explicit old-key fallback. Add rotate as an optional helper argument.
  • Hint 1: Update configuration after the old cookie is issued.
  • Hint 2: Keep the tamper rejection observable.

Solution, tests and trade-offs

Baseline cases cover two values and a corrupted signature.

Baseline cases cover two values and a corrupted signature. Practice exercises rotation with intact and tampered cookies.

Verify the session read response, not merely the Set-Cookie header. These checks demonstrate the configured signing behavior on this pinned version; they do not test browser transport, deployment-wide coordinated key rotation or revocation of every previously issued session.

Practice solution and acceptance checks

python

from flask import Flask, session

def session_roundtrip(value, tamper=False, rotate=False):
    app = Flask(__name__)
    app.config.update(TESTING=True, SECRET_KEY='owned-course-key')
    @app.get('/set')
    def store():
        session['theme'] = value
        return {'saved': True}
    @app.get('/get')
    def read():
        return {'theme': session.get('theme', 'default')}
    client = app.test_client()
    client.get('/set')
    if tamper:
        cookie = client.get_cookie('session').value
        client.set_cookie('session', cookie + 'broken')
    if rotate:
        app.config.update(SECRET_KEY='new-owned-key', SECRET_KEY_FALLBACKS=['owned-course-key'])
    response = client.get('/get')
    return response.json
Reasoning
Baseline cases cover two values and a corrupted signature. Practice exercises rotation with intact and tampered cookies.

Interview reasoning and next step

Flask 3.1 introduced SECRET_KEY_FALLBACKS, while 3.1.3 includes a session-related Vary: Cookie fix.

Flask 3.1 introduced SECRET_KEY_FALLBACKS, while 3.1.3 includes a session-related Vary: Cookie fix. Keep framework security updates current and examine caching whenever a response depends on session state.

A public shared cache must not mix personalized pages. If immediate revocation or large server-controlled state is required, evaluate a server-side session design with explicit retention and access controls.

Optional stretch — after the core task works: Document fallback retirement and coordinated multi-worker rollout.

Computer practice: https://applydjinn.com/typorithm/python-frameworks-lab-20261105.zip. Install the pinned dependencies in an isolated environment, run verify.py --track flask, then edit the retained exercise baseline and check it with --practice fl-sessions. The lab uses owned in-memory databases, local framework clients and tiny CPU tensors. It never reads the working ApplyDjinn database, contacts production feeds or downloads models.

Understand that default cookie sessions are signed, not encrypted.