Sessions and Tokens — From the Browser to the Mesh
Issue a __Host- session cookie and delete it properly
Goal
Stand up a login server at 127.0.0.1:8301 to issue, check, and revoke a __Host-sid session cookie, and confirm down to whether the cookie is actually accepted into and deleted from the browser (curl) store.
Why it matters
Cookie problems do not show up in the server log. If the server sent Set-Cookie normally but the browser silently discards it because of a prefix rule or the Secure condition, the symptom appears only as "sometimes login doesn't work". That is why this lab looks at the curl cookie jar together with the response headers. curl 8.5 also does not store a cookie that breaks the __Host- and __Secure- rules, so whether it remained in the jar is an approximation of whether it was accepted. You look at logout with the same eyes. Deleting a cookie is only a request to the browser, and for someone who copied the value, all that matters is whether the server record was erased.
Steps
- Bring up
/root/st/cookie/app.pyat 127.0.0.1:8301.GET /healthzreturns 200 and{"ok":true}. - When you send JSON
{"user":"alice","password":"wonderland"}toPOST /login, it returns 200 and{"user":"alice"}and issues a__Host-sidcookie. The value is random of at least 128 bits made withsecrets(at least 22 base64url characters), must differ on every login, and must not contain the user name.bob/buildercan log in too. - Attach
Secure,HttpOnly,SameSite=Lax, andPath=/to the session cookie and do not attachDomainorMax-Age/Expires(a host-only session cookie). Log in as alice and leave the cookie jar withcurl -c /root/st/cookie/jar.txt. The__Host-sidline of that file must start with#HttpOnly_127.0.0.1, with path/, SecureTRUE, and expiry0. GET /mereturns 200 and{"user":"<사용자>"}(the placeholder is the user name) if there is a valid__Host-sid, and returns 401 if there is no cookie or the value was never issued by the server.POST /loginwith a wrong password (alice/wrongpass) and with a nonexistent user (mallory) is a 401 and must have noSet-Cookieline at all.POST /logoutreturns 200 and erases the server-side session. The response must have aSet-Cookie(Max-Age=0) that deletes__Host-sid, and the curl jar must actually accept that deletion so that the cookie disappears. If you callGET /mewith the cookie value you copied before logging out, it must be 401. Do not overwrite thejar.txtof step 3; test with a different jar file.- When the Set-Cookie values below arrive in the response of
https://app.example.com/login, judge whether the browser stores the cookie and write one line each into/root/st/cookie/prefix.txtas번호=acceptor번호=reject(the placeholder is the item number).- 1
__Host-sid=a1; Secure; HttpOnly; Path=/; SameSite=Lax - 2
__Host-sid=a2; HttpOnly; Path=/; SameSite=Lax - 3
__Host-sid=a3; Secure; Path=/; Domain=app.example.com - 4
__Host-sid=a4; Secure; Path=/account - 5
__Secure-theme=dark; Secure; Path=/account; Domain=example.com - 6
__Secure-theme=dark; Path=/ - 7
__host-sid=a7; Path=/ - 8
theme=dark; Path=/; Domain=example.com
- 1
- With
/root/st/cookie/e2e.sh, test in order login, /me with the cookie, /me without the cookie, a wrong password, logout, and reuse of the old cookie, then count the__Host-sidlines left in the jar after logout and writelogin=200 me=200 no_cookie=401 bad_password=401 logout=200 replay=401 jar_after_logout=0as a single line in/root/st/cookie/e2e.out.
Notes
- Viewing the response headers:
curl -s -D - -o /dev/null ...Keeping and writing the jar:curl -c <파일> -b <파일> ...(the placeholder is the jar file name) - A curl jar file has seven tab-separated columns. You can pull out the columns with
awk -F'\t'. - curl 8.5 in this Pod also stores and sends back a Secure cookie received over
http://127.0.0.1. A real browser needs HTTPS (localhost is an exception). - Common mistake 1: deleting only the cookie at logout and leaving the server session — the copied value keeps working.
- Common mistake 2: leaving
SecureorPath=/off the deletion cookie — it gets caught by the__Host-rules, the deletion itself is ignored, and the old cookie remains.
Bring up the login server
Bring up /root/st/cookie/app.py at 127.0.0.1:8301. GET /healthz returns 200 and {"ok":true}.
The standard library http.server is enough. Start the server in the background, wait until /healthz answers, and then move on.
Issue a session cookie at login
When you send JSON {"user":"alice","password":"wonderland"} to POST /login, it returns 200 and {"user":"alice"} and issues a __Host-sid cookie. The value is random of at least 128 bits made with secrets (at least 22 base64url characters), must differ on every login, and must not contain the user name. bob/builder can log in too.
The session value is a key that carries no meaning. The random module is predictable, so look in the secrets module, which uses cryptographic randomness, for a function that makes a URL-safe string. The server records in a dict whose session it is.
Check the cookie attributes and the jar
Attach Secure, HttpOnly, SameSite=Lax, and Path=/ to the session cookie and do not attach Domain or Max-Age/Expires (a host-only session cookie). Log in as alice and leave the cookie jar with curl -c /root/st/cookie/jar.txt. The __Host-sid line of that file must start with #HttpOnly_127.0.0.1, with path /, Secure TRUE, and expiry 0.
The more you write Domain, the wider it gets. You have to omit it for it to go only to the host that sent it. If you do not write an expiry, it becomes a session cookie that disappears when the browser closes. A curl jar file has seven tab-separated columns, and an HttpOnly cookie has a mark in front of the first column.
Recognize the user by the cookie
GET /me returns 200 and {"user":"<사용자>"} (the placeholder is the user name) if there is a valid __Host-sid, and returns 401 if there is no cookie or the value was never issued by the server.
The mere fact that a cookie came attached proves nothing. You have to use that value to look up the server's session record and pull out the user.
Do not give a cookie on a failed login
POST /login with a wrong password (alice/wrongpass) and with a nonexistent user (mallory) is a 401 and must have no Set-Cookie line at all.
If the code that makes the cookie comes before the password check, a cookie goes out even on failure. It is better to compare the password with a function that does not leak timing differences.
Make logout real
POST /logout returns 200 and erases the server-side session. The response must have a Set-Cookie (Max-Age=0) that deletes __Host-sid, and the curl jar must actually accept that deletion so that the cookie disappears. If you call GET /me with the cookie value you copied before logging out, it must be 401. Do not overwrite the jar.txt of step 3; test with a different jar file.
Deleting a cookie is only a request. For someone who copied the value, all that matters is whether the server record was erased. And a deletion cookie is also a cookie, so it has to pass the conditions of the __Host- prefix again for the browser to accept it.
Judge the prefix rules
When the Set-Cookie values below arrive in the response of https://app.example.com/login, judge whether the browser stores the cookie and write one line each into /root/st/cookie/prefix.txt as 번호=accept or 번호=reject (the placeholder is the item number). 1 __Host-sid=a1; Secure; HttpOnly; Path=/; SameSite=Lax · 2 __Host-sid=a2; HttpOnly; Path=/; SameSite=Lax · 3 __Host-sid=a3; Secure; Path=/; Domain=app.example.com · 4 __Host-sid=a4; Secure; Path=/account · 5 __Secure-theme=dark; Secure; Path=/account; Domain=example.com · 6 __Secure-theme=dark; Path=/ · 7 __host-sid=a7; Path=/ · 8 theme=dark; Path=/; Domain=example.com
First write down the list of conditions each prefix requires and check the lines against it one by one. __Host- has three and __Secure- has one. Also check in the specification whether the browser compares the prefix case-sensitively. A line that starts with # can be left as a comment.
Prove the whole flow at once
With /root/st/cookie/e2e.sh, test in order login, /me with the cookie, /me without the cookie, a wrong password, logout, and reuse of the old cookie, then count the __Host-sid lines left in the jar after logout and write login=200 me=200 no_cookie=401 bad_password=401 logout=200 replay=401 jar_after_logout=0 as a single line in /root/st/cookie/e2e.out.
You just run the checks of the earlier steps in a row with a single cookie jar. The old value to use for the reuse test has to be pulled out of the jar before you log out.