Start learning free

LESSON 10 OF 20 · SECURITY: TOKENS AND SESSIONS

A New Session at Login

Practice

This shop keeps sessions on the server: SESSIONS maps a random id to the visitor's data, and the id travels in the sid cookie. load_session gives every new visitor a session, so guests can fill a cart, and save_session puts g.sid into the cookie after each request.

The hole: at login, the starting code writes the email into the session the browser already has, and an attacker can choose that session. They open the shop on a library computer, copy its sid cookie and walk away. When Ana logs in on that computer, the attacker's own browser, sending the same id, is logged in as Ana: it sees her account and can order with it. This is session fixation.

The fix: give the visitor a new session id at login. Delete the old session, make a new random id and store the email under it; the id the attacker copied then names nothing. Do it again at logout, so the next person at the computer starts clean. Here is the same move for a shop worker who unlocks the admin pages, keeping the session's data but not its id:

@app.post("/admin/unlock")
def unlock():
    # ...after checking the admin password...
    data = SESSIONS.pop(g.sid)
    g.sid = secrets.token_urlsafe(16)
    SESSIONS[g.sid] = data
    SESSIONS[g.sid]["admin"] = True
    return jsonify({"message": "Admin pages unlocked"})

The check runs two browsers, each with its own cookies. Before the login it copies the attacker's cookie into the victim's browser, just like the library computer:

POST /cart    {"product_id": 3}  (attacker)  →  200  {"cart": [3]}
POST /login   {"email": "ana@example.com", "password": "ana-pass-123"}  (victim, with the attacker's cookie)  →  200  {"message": "Logged in"}
GET /me       (victim)  →  200  {"email": "ana@example.com"}
GET /me       (attacker)  →  401  {"error": "Login required"}
POST /cart    {"product_id": 1}  (victim)  →  200  {"cart": [1]}
POST /logout  (victim)  →  200  {"message": "Logged out"}
POST /cart    {"product_id": 4}  (victim)  →  200  {"cart": [4]}
GET /me       (victim)  →  401  {"error": "Login required"}

Your task: write fresh_session(): it deletes the current session, sets g.sid to a new id from secrets.token_urlsafe(16) and stores an empty session, {"cart": []}, under it. Then call it in login, before the email is stored, and in logout, after the email is removed.


Assignment
Follow the Coding Tutorial to give the visitor a new session id at login, and again at logout.


Hint
Delete the old session while g.sid still holds the old id, then change g.sid. You don't touch the cookie yourself: save_session sends whatever g.sid holds.


STEP 1 OF 6
    Ctrl + Enter to check