BackendLesson 2 of 49 min

Sessions, cookies, and tokens

How a stateless protocol manages to remember you at all.

Recall from the first course that every request starts from nothing. So how does a site know you are still logged in on page seven? Because something small is attached to every single request that identifies you, and that something is almost always a cookie.

Staying logged in
  1. Prove it once
    You send your password. The server checks it.
  2. Get a token
    The server sends back a long unguessable string.
  3. Browser stores it
    A cookie is kept and attached automatically to every later request.
  4. Server recognises it
    Each request arrives with the token and is identified.

There are two flavours of that token. A session id is meaningless on its own, just a ticket number the server looks up in its own records. A signed token (a JWT is the common one) contains the information itself, cryptographically signed so it cannot be tampered with, and needs no lookup.

Session id

  • Server looks it up every request
  • Logging out is instant and reliable
  • Requires shared storage across servers

Signed token

  • No lookup needed, scales easily
  • Cannot be revoked before it expires
  • Anyone holding it can read what is inside

What to remember

  • A cookie carries an identifier on every request, which is how memory happens.
  • Session ids are looked up; signed tokens carry their own data.
  • Signed tokens are hard to revoke, so they must be short-lived.

Terms in this lesson

Field notes

Loaded from a deliberately slow source. The lesson above was already readable while this was still travelling. That is streaming, and it is the same trick a chat interface uses.

The URL that read everyone’s invoices

An invoice page checked that you were logged in and then loaded whatever id was in the address. Changing the number showed somebody else’s invoice. It was found by a customer who mistyped, not by a review.

Classic IDOR, still extremely common

The key in the client bundle

An API key was placed behind the public environment prefix so it would be readable from the frontend. It worked. It was also visible to every visitor, and was being used by strangers within a week.

Scraped from a public bundle

resolved in 900ms · region iad1

Hide field notes toggles a search param the loader reads. With it off, the slow promise is never created, so nothing streams.