2.14. Sessions
Table of Contents
Why Sessions Exist
HTTP is a stateless protocol. Each request is independent. The server does not remember that the same browser sent a previous request.
Yet, most web apps need to remember things across requests, for example:
- Which user is logged in
- What is in a shopping cart
- User preferences like language or theme
- Multi-step forms or wizards
A session is a way to store data on the server and connect it to a particular client across multiple requests.
You can think of it as:
- Session data: a small dictionary of key-value pairs stored on the server, for example:
{"user_id": 42, "cart_items": [1, 5, 7]} - Session ID: a random unique identifier, for example:
f3a7c9b2e1... - Session token in client: the browser stores only the Session ID, often in a cookie, then sends it with every request
So the pattern is:
- Server creates a session and assigns it a session ID
- Server sends the session ID to the client (usually via a cookie)
- Client sends the session ID back with every request
- Server looks up session data using that ID
A session allows a stateless protocol (HTTP) to support stateful interactions by storing state on the server and identifying it with a session ID from the client.
Sessions vs Cookies
Sessions and cookies often appear together, but they are not the same thing.
- Cookies are a browser feature
- Sessions are an application / server concept
Core Difference
| Feature | Cookies | Sessions |
|---|---|---|
| Stored where | In the browser | On the server |
| Who controls data | Mainly the client (but set by server) | The server |
| What is stored | Small pieces of text | Any server data (objects, lists, flags, etc.) |
| Security | Visible to the user | Hidden from user, more secure |
| Typical usage | Preferences, session IDs, simple flags | User login, shopping cart, temporary state |
With sessions:
- The session ID is sent to the browser, usually in a cookie
- The session data stays on the server
Example:
- Cookie in browser:
session_id=abc123xyz - On server (in memory, DB, Redis, etc.):
abc123xyz => {
"user_id": 10,
"role": "admin",
"cart": [101, 202]
}The browser never sees the actual data. It only knows the ID.
How Sessions Work in the Request-Response Cycle
Let us walk through a typical login flow using sessions.
Step 1: User Logs In
- User submits login form:
POST /loginwith username and password. - Server checks credentials.
- If valid:
- Create a new session record on the server
- Generate a random
session_id, for example:d8a2f117-5e5a-4c0a-9a8f - Store session data on the server:
sessions["d8a2f117-5e5a-4c0a-9a8f"] = {
"user_id": 42,
"username": "alice"
}- Server sends a response with a Set-Cookie header:
HTTP/1.1 200 OK
Set-Cookie: session_id=d8a2f117-5e5a-4c0a-9a8f; HttpOnly; Secure; Path=/;- Browser stores this cookie.
Step 2: Browser Sends Session Cookie on Each Request
Next request, for example GET /profile:
GET /profile HTTP/1.1
Host: example.com
Cookie: session_id=d8a2f117-5e5a-4c0a-9a8fOn the server:
- Read
session_idfrom cookies - Look it up in session storage:
session = sessions.get("d8a2f117-5e5a-4c0a-9a8f")- If found, server knows the user is logged in as
user_id=42 - Server can then load the profile for user 42
If there is no session for that ID (for example expired or invalid), the server treats the request as from an anonymous user.
Step 3: Modifying Session Data
On POST /cart/add:
- Server gets
session_idfrom cookie - Looks up session:
session = sessions.get("d8a2f117-5e5a-4c0a-9a8f")
session["cart"].append(new_item_id)- Saves updated session back to storage
From the browser side, nothing changes. It still only sends session_id.
Step 4: Logging Out and Session Destruction
On POST /logout:
- Server reads the
session_idcookie - Deletes the session from storage:
sessions.pop("d8a2f117-5e5a-4c0a-9a8f", None)- Optionally tells browser to delete the cookie:
Set-Cookie: session_id=deleted; Max-Age=0; Path=/;
From then on, the session_id no longer points to valid data, so the user is effectively logged out.
Session Storage Options
Sessions can be stored in different places on the server side. Each option has tradeoffs.
| Storage Type | Where it lives | Pros | Cons |
|---|---|---|---|
| In memory | RAM of the app server | Very fast, simple | Lost on restart, hard with multiple servers |
| File based | Local disk | Simple, persists across restarts | Slower, not good for many servers |
| Database | Relational / NoSQL DB | Persistent, scalable | Adds DB load and some latency |
| Cache store | Redis, Memcached | Very fast, good for scaling | Extra infrastructure, sessions might be lost |
For a single small app:
- In memory or file-based sessions are often acceptable
For production apps with many users and multiple servers:
- Centralized storage such as Redis or a database is common
Example of how mapping works conceptually:
| session_id | session data |
|---|---|
d8a2f117-5e5a-4c0a-9a8f | {"user_id": 42, "cart": [101, 202]} |
5d44ec83-d163-476b-b658-7c8fbfb6 | {"user_id": 19, "theme": "dark"} |
This table can live in memory, Redis, PostgreSQL, etc.
Session IDs and Security
The entire security of sessions depends on the session ID. If an attacker gets it, they can act as that user.
So session IDs must be:
- Random and unpredictable, not simple counts like
1,2,3 - Long enough to prevent guessing
- Protected in transit using HTTPS
- Stored securely in cookies with security flags
The session ID is as sensitive as a password for the lifetime of the session. If someone steals it, they can hijack the session and impersonate the user.
Common protections:
- Use a cryptographically secure random generator for IDs
- Use HTTPS so session cookies cannot be easily sniffed
- Use cookie security flags such as:
HttpOnlyto prevent JavaScript from reading the cookieSecureto only send cookie over HTTPS- Regenerate session IDs on login or role change to reduce fixation attacks
- Set session expiration so old sessions eventually die
Session Lifetime and Expiration
Sessions should not live forever.
Types of expiration
- Absolute expiration
- Session expires after a fixed time, for example 24 hours from creation
- Even if user is active, session eventually ends
- Idle timeout
- Session expires if there is no activity for a period, for example 30 minutes
- Each request can reset the timer
- Manual invalidation
- The server can invalidate sessions explicitly
- For example on logout, password change, admin action
On the server, you might store timestamps:
session = {
"user_id": 42,
"created_at": 1724700000, # Unix timestamp
"last_seen": 1724706000
}Then on each request:
- Check
created_atandlast_seen - If too old, delete session and force login
Example rule:
A common policy:
- Absolute expiration: $24 \text{ hours}$
- Idle timeout: $30 \text{ minutes}$
Whichever limit is hit first ends the session.
Stateless vs Stateful Sessions
There are two broad approaches to sessions.
1. Stateful Sessions
This is what we have described so far:
- Server keeps session data
- Client only has a session ID
Characteristics:
- Stateful server, because it must remember sessions
- Easy to invalidate a session by deleting it server side
- Can store arbitrary data in session
- Harder to scale if stored in only one server, needs shared storage
Most classic web frameworks (Django, Rails, etc.) use stateful sessions.
2. Stateless Sessions
Here, the session data itself is stored on the client side, typically in a signed or encrypted token such as a JSON Web Token (JWT).
- Server does not store session state
- Server only verifies token and reads data from it
Characteristics:
- Server is stateless concerning sessions
- Easier to scale horizontally, no shared session storage
- Harder to invalidate a token before it expires, because server might not keep a list of them
- Token size and content must be carefully designed
For this chapter, the main idea is the contrast:
| Aspect | Stateful sessions | Stateless sessions |
|---|---|---|
| Data lives | On the server | In token on client |
| Client stores | Session ID | Whole session token |
| Scaling | Needs shared store or stickiness | Easier to scale, no shared store needed |
| Invalidation | Easy, delete on server | Harder, often requires blacklists |
You will see more details when you study tokens and JWTs later.
Session Use Cases and Examples
To solidify the concept, here are some typical session uses.
Example 1: Remembering Login State
Without sessions:
- After login, the server would not know who you are on the next request
With sessions:
- After successful login:
session["user_id"] = user.id
session["username"] = user.username- On any route requiring authentication:
if "user_id" not in session:
return redirect("/login")
The framework hides the cookie and lookup details from you. You work with the session object.
Example 2: Shopping Cart
You can implement a simple cart without requiring the user to log in.
- When a new visitor arrives without a session, create one and assign an ID.
- Store cart items in session:
session.setdefault("cart", [])
session["cart"].append(product_id)- When displaying cart:
cart_items = session.get("cart", [])If the user closes the browser and returns before session expiration, the cart is still there.
Example 3: Flash Messages
Some frameworks use sessions for one-time messages, for example:
- "Your profile has been updated"
- "Invalid password"
Implementation idea:
- On POST /profile/update success:
session["flash"] = "Profile updated successfully."- On next page load:
message = session.pop("flash", None) # read and remove- Show
messageif it exists
This uses the session as temporary storage between two requests.
Common Pitfalls with Sessions
Beginners often make similar mistakes when working with sessions.
1. Storing Too Much Data
Sessions are for small pieces of data. Avoid storing:
- Large objects
- Files
- Big lists or query results
Problems:
- Memory or storage growth
- Slower lookups
- More network traffic if using a remote session store
Keep it small:
- User ID, not full user object
- Product IDs, not full product details
2. Relying Only on Sessions for Security
Session checks are important, but you still need:
- Proper authorization checks
- Input validation
- Protection against CSRF and other attacks
A valid session does not mean the request is safe.
3. Forgetting to Clear Sessions on Sensitive Changes
If a user:
- Changes password
- Disables their own account
- Changes permissions or role
You might want to:
- Invalidate existing sessions
- Force re-login
Otherwise, old sessions might still be valid.
4. Not Using HTTPS with Session Cookies
Sending session cookies over plain HTTP exposes them to eavesdropping.
Always protect real user sessions with HTTPS and the Secure cookie flag.
Summary
- HTTP is stateless, so it does not remember users across requests.
- Sessions allow the server to remember data across multiple requests from the same client.
- A session typically uses:
- Session data on the server
- A session ID stored in a cookie on the client
- Cookies are the delivery mechanism, sessions are the data model on the server.
- Sessions can be stored in memory, files, databases, or cache stores like Redis.
- Session security focuses on protecting the session ID, using randomness, HTTPS, and cookie flags.
- Sessions have lifetimes and timeouts to limit risk and resource use.
- There are stateful and stateless approaches to sessions, each with tradeoffs.
- Sessions power common features like login, carts, and flash messages.
Understanding sessions gives you the missing piece for building web applications that feel persistent and personalized on top of a stateless protocol.
Views: 9
KAHIBARO