Sessions and Tokens — From the Browser to the Mesh
Redis sessions and session fixation defense
Goal
Stand up a login server with Redis attached as the session store, and confirm with real HTTP requests that the session ID is regenerated at the moment of login, that absolute expiry works regardless of idle, and that logout invalidates immediately.
Why it matters
A server session holds the state on the server and gives the browser only a key. So you can invalidate it immediately with a single logout, but if that key does not change before and after login, session fixation becomes possible, in which someone rides onto my login using a key they planted in advance. OWASP insists on regenerating the session ID at the moment the privilege level changes from anonymous to authenticated. Also, expiry needs both idle (expires with no activity, renewed on every request) and absolute (expires unconditionally once it is old enough), and since Redis TTL alone cannot express absolute, you put the creation time separately inside the value. This lab makes you prove for yourself that these rules hold in real requests.
Steps
- Bring up
/root/st/session/app.pyat 127.0.0.1:8302.GET /healthreturns 200 and{"ok":true}. - Save, as a single Set-Cookie header line in
/root/st/session/cookie.txt, whether the session cookie has the name__Host-sidwithSecure,HttpOnly,SameSite, andPath=/. - Get an anonymous session and write into
/root/st/session/store.txtwhether that ID exists in Redis under the keysess:<id>and has a TTL and a user. - If you log in carrying the anonymous session ID as it is, write into
/root/st/session/fixation.txtwhether the response gives a different session ID and the old ID disappears. - Write into
/root/st/session/idle_ttl.txtwhether the login session's TTL is at or below the idle limit and fills back up with a request. - Write into
/root/st/session/absolute.txtwhether a session withcreatedset to the past is not accepted as authenticated even when idle TTL remains. - Write into
/root/st/session/logout.txtwhether, after logout, the same cookie falls to anonymous and the store key disappears. - Test the whole flow with
/root/st/session/e2e.shand leave four lines in/root/st/session/e2e.out.
Notes
- The session ID is made with
secrets.token_urlsafe(32)(256 bits, exceeding OWASP's minimum of 64 bits). - idle is judged as sliding by pushing the TTL with
EXPIREon every request, and absolute bycreatedinside the value. - Common mistake 1: not regenerating the session ID at login — session fixation still holds.
- Common mistake 2: expressing absolute only with a TTL — it gets pushed on every request and never dies.
Bring up the Redis session server
Bring up /root/st/session/app.py at 127.0.0.1:8302. GET /health returns 200 and {"ok":true}.
Redis is already running on 6379. Start the server in the background and poll until it is ready.
Check the session cookie attributes
Save, as a single Set-Cookie header line in /root/st/session/cookie.txt, whether the session cookie has the name __Host-sid with Secure, HttpOnly, SameSite, and Path=/.
The __Host- prefix requires Secure and Path=/ and forbids attaching Domain. Look at the response headers with curl -D - .
That the session is stored in Redis
Get an anonymous session and write into /root/st/session/store.txt, as sid= redis_key= exists=1 ttl=<양수> user=(anon) (the placeholder is a positive number), whether that ID exists in Redis under the key sess:<id> and has a TTL and a user.
Capture the session ID with the cookie jar (-c) and check it with redis-cli EXISTS/TTL/GET.
Regenerate the session ID at login
If you log in carrying the anonymous session ID as it is, write into /root/st/session/fixation.txt, as before= after= changed=yes old_gone=yes, whether the response gives a different session ID and the old ID disappears from the store.
This is the heart of the session fixation defense. On a successful login you must delete the old session and issue a new ID for changed=yes to come out.
Idle timeout is a sliding TTL
Write into /root/st/session/idle_ttl.txt, as ttl_before= ttl_after_request=, whether the login session's Redis TTL is at or below the idle limit and fills back up when you send one more request. Both must be positive, and after must be near the limit.
If you push the TTL out again with EXPIRE on every request, it becomes a sliding idle.
Absolute expiry is independent of TTL
Write into /root/st/session/absolute.txt, as code=200 auth=False gone=yes, whether a session with created set further in the past than the absolute limit and with plenty of idle TTL left is not accepted as authenticated at GET /whoami.
In load_session, if now - created > ABSOLUTE_SEC, you must reject it even when idle TTL remains.
Invalidate immediately at logout
After logging in, log out and then do GET /whoami with the same cookie, and write into /root/st/session/logout.txt, as revoked_auth=False key_gone=yes, whether it falls to anonymous and the store key disappears.
Because the server holds the state, deleting one key invalidates it immediately — this is the difference from a JWT.
Prove the whole flow at once
With /root/st/session/e2e.sh, test in order anonymous, then login regeneration, then authentication, then logout invalidation, and leave reissue=yes, old_gone=yes, logged_in=True, and after_logout=False in /root/st/session/e2e.out.
Chain the earlier steps together in one script. All four lines must come out.