Cookies and Sessions
The Theme-Park Wristband
At a theme park you show your ticket once at the gate and get a wristband. After that, every ride just looks at the band. The band says who you are and when it was issued, and it carries a tamper-proof stamp that only the park can make. If you scribble "VIP" on it, the stamp no longer matches and the ride operator turns you away.
A session cookie is that wristband. After login, the server hands the
browser a token; the browser shows it on every request. SessionSigner
makes the stamp with a secret only the server knows.
Try It
from pyweb.security import SessionSigner, csrf_matches, new_csrf_token
now = [1_000_000.0]
signer = SessionSigner("demo-key-not-real-0123456789abcdef", max_age=60, clock=lambda: now[0])
token = signer.sign({"user": "ada"})
print(signer.unsign(token))
payload, stamp = token.split(".")
print(signer.unsign("X" + payload[1:] + "." + stamp))
now[0] += 61
print(signer.unsign(token))
csrf = new_csrf_token()
print(csrf_matches(csrf, csrf), csrf_matches(csrf, None))
Output:
The demo secret above is fake. A real secret is at least 32 random characters, kept out of your code (for example in an environment variable) and never printed or logged.
The Rules
- Tamper-proof. Change one character of the token and
unsignreturnsNone; treat that visitor as a stranger. - Expiry. Each token records when it was issued (
iat). Aftermax_age(8 hours by default) it stops working, so a stolen band does not work forever. - HttpOnly. When you set the cookie, add
HttpOnlyso page JavaScript cannot read it (a hostile script cannot steal what it cannot see). AddSecureon HTTPS andSameSite=Laxto limit cross-site sending. - Stamped is not secret. The token is signed, not hidden. Anyone can read it, so never put passwords inside.
- CSRF. A form carries a random
new_csrf_token()too, and the server checks it withcsrf_matches, so another website cannot post forms for you.
What We Test
- A signed token comes back with its data and issue time.
- One changed character in either half is rejected.
- A different secret, bad base64, bad JSON and wrong shapes give
None. - Tokens expire with a fake clock; far-future tokens are refused.
- Short secrets are refused; CSRF compare handles
Noneand empty.
Next Up
Keep guessers out too: see Passwords for hashing and the login throttle.