Session management and cookies
HTTP has no memory: each request arrives as if from a stranger. To keep you logged in as you click around, the application hands you a token after login and expects you to show it every time. Whoever holds that token is you, as far as the server is concerned. Protecting it is the whole point of session management.
Reminder
Reading or reusing someone else’s session token is impersonation of that person. You will only examine the cookies of your own test accounts in your own lab. If you ever find a session weakness in a real application, you report it through an authorised channel, you do not use it.
The coat-check analogy
At a coat-check you hand over your coat and receive a numbered ticket. Later, whoever presents the ticket gets the coat. The clerk does not recognise your face; the ticket is the only proof. A web session works the same way: after a successful login, the server creates a record, gives you a session identifier and stores it in a cookie in your browser. Each request carries the cookie, and the server looks up who it belongs to.
HTTP/1.1 200 OK
Set-Cookie: session=7f3a9c0d...; Path=/; Secure; HttpOnly; SameSite=Lax
GET /account HTTP/1.1
Cookie: session=7f3a9c0d...
This design implies two requirements. The ticket must be impossible to guess or forge, and it must be hard to steal, since possession equals identity.
Cookie security attributes
A cookie can carry attributes that tell the browser how to handle it. They are your first checklist when inspecting a session.
| Attribute | What it does | Risk if missing |
|---|---|---|
Secure | The cookie is only sent over HTTPS | It can travel in clear text on an unencrypted connection |
HttpOnly | JavaScript cannot read the cookie | A script injected into the page (see XSS, chapter 4) can steal it |
SameSite | Limits sending the cookie on cross-site requests (Strict, Lax or None) | Easier cross-site request forgery (chapter 4) |
Path and Domain | Define which URLs and sub-domains receive the cookie | Too broad a scope exposes it to more applications |
Expires / Max-Age | Lifetime of the cookie | A cookie living for months is a long window for theft |
The MDN web docs at developer.mozilla.org document each attribute in detail and are a reliable reference. Note that HttpOnly does not prevent every attack; it simply removes one easy way to steal the cookie.
Browsers also support cookie name prefixes such as __Host- and __Secure-. A cookie whose name starts with __Host- is only accepted if it is Secure, has Path=/ and carries no Domain attribute, which prevents other sub-domains from overwriting it. It is a neat, inexpensive hardening step, and spotting its absence on a sensitive cookie is a useful low-severity observation for your notes.
Weaknesses to look for
Predictable identifiers
If the session identifier is built from something guessable, like an incrementing number, a user name or a timestamp, an attacker could generate valid ones. Good identifiers are long, random values produced by a cryptographically secure generator, with enough entropy that guessing is infeasible. As a tester, log in several times, collect the identifiers, and look for patterns. A good one looks random each time; a poor one reveals structure.
Session fixation
Imagine an attacker who gets you to use an identifier they already know, then waits for you to log in. If the application keeps the same identifier after login, the attacker now holds a logged-in ticket. The fix is straightforward: issue a new identifier at login (and after any privilege change). To test, note your cookie before logging in and compare it after. If it is unchanged, report session fixation risk.
Incomplete logout
Click “log out”, then, in Repeater, replay a request that carries the old cookie. If it still works, the server did not invalidate the session; it only removed the cookie from the browser. Logout must destroy the session on the server side. The same goes for password changes: other sessions should be ended.
Missing timeouts
A session that never expires on inactivity is dangerous on shared computers. Look at both an idle timeout and an absolute lifetime.
Session data stored in the cookie
Some applications put the user’s role or identifier directly in a cookie, unprotected. If the application trusts it, a user could change it. The server should store the sensitive data and the cookie should hold only the opaque identifier, or, if data is carried in the token, it must be cryptographically signed and verified.
A note on tokens such as JWT
Modern applications and APIs often use JSON Web Tokens, a signed token carrying claims such as the user ID and expiry. A signed token is not an encrypted one: its content can be read by anyone who has it, so it must not contain secrets. The signature is what prevents modification, which means the server must always verify it, check the expiry and use strong, properly managed keys. Misconfigured verification is a known source of serious flaws. At this level, remember the principle: the client holds the token, so the server must check it on every request.
Inspecting cookies in practice
Open your browser’s developer tools, go to the storage or application panel and select cookies. For each one, read the name, value and attributes. In Burp, the Set-Cookie response header after login shows the same information. Questions to ask:
- Which cookie carries the session? Does it look random?
- Does it have
HttpOnly,Secureand aSameSitevalue? - Does the value change after login?
- Does the session survive logout?
- Are there other cookies holding data that look like roles, prices or identifiers?
Remember that your lab runs over plain HTTP, so a Secure cookie may behave unusually there. Note this in your notebook and treat it as a lab limitation rather than a finding.
Tip
The framework’s default session handling is usually well designed. Most real flaws appear when developers write their own session system. Report “custom session mechanism” as a risk to review in a code audit.
Your turn: audit the session of your lab application
Log in to your training application with a test account and inspect its session cookie. Record its attributes, compare it before and after login, log out and test whether the old cookie still works (using Repeater against your own lab). Conclude with a short list of findings and recommendations.
See a solution
An example set of notes for a deliberately weak application:
Cookie: PHPSESSID (random-looking, 26 characters) - OK
Attributes: no HttpOnly, no Secure (lab runs on HTTP), SameSite missing -> findings
Before/after login: identifier unchanged -> session fixation risk
After logout: old cookie replayed in Repeater still returns the account page -> server-side session not destroyed
Extra cookie: "security=low" (DVWA setting) - lab only, ignore
Recommendations: regenerate the ID at login, destroy the session on logout, set HttpOnly, Secure and SameSite
Compare with the DVWA “impossible” level to see how a safer configuration is expressed in code.
Key takeaways
- A session token proves identity: it must be random, unguessable and well protected.
- Check cookie attributes:
Secure,HttpOnly,SameSite, scope and lifetime. - Test for predictable identifiers, session fixation, incomplete logout and missing timeouts.
- Issue a new identifier at login and destroy the session server-side at logout.
- Signed tokens such as JWT can be read by clients, so never store secrets in them, and always verify them on the server.
Next we look at a feature that bypasses all of this if badly done: password reset.