Skip to content

Tokens, cookies, and what is actually secret

Base64 is not encryption and a JWT is signed, not hidden — both are readable by anyone holding them.

Lesson 4 of 4 · about 3 minutes

Base64 is an encoding, not a secret

Base64 turns bytes into characters that survive being put in text — a URL, a header, an email. It is trivially reversible by design and provides no protection whatsoever.

Every so often a password or a key turns up base64-encoded in a config file, treated as hidden. It is not hidden; it is written in a slightly inconvenient alphabet. Anything base64 can be decoded by anyone holding it, in one step, with no key.

A JWT is signed, and that means readable

A JWT is three base64 sections: a header, a payload of claims, and a signature. The signature proves the first two were not altered by someone without the key. It does not hide them.

So a JWT payload is readable by anyone who has the token, including the browser it was issued to. Putting anything private in it — an internal id you would not publish, a role you would not admit to, anything about another person — is publishing it to whoever holds the token.

What the signature gives you is integrity: change a claim and the signature no longer matches, so a server that verifies it will refuse. A server that decodes without verifying is trusting a value anyone can edit, which is one of the most common authentication bugs there is.

Check the expiry too. A token with no exp, or one a server does not check, is valid forever — and a token stolen once is then useful forever.

Cookies and where a token should live

A cookie is sent automatically with every matching request, which is convenient and is exactly what makes CSRF possible: another site can cause a request that carries your cookie. SameSite=Lax or Strict is what stops that, and Secure keeps it to HTTPS.

HttpOnly keeps a cookie away from JavaScript, so a cross-site scripting bug cannot read it. A token in localStorage has no such protection: any script on the page can take it, including one that arrived through a bug in a dependency. That is the real trade — cookies need CSRF defences, tokens in JavaScript need you to be sure nothing hostile ever runs on the page.

Worked examples

  • Read a JWT payload without any key at all

    /header.payload.signature/

    Against: Three base64 sections separated by dots

    The first two decode in one step with no key. Anyone holding a token can read its claims — the signature stops edits, not reading.

Check yourself

Practice questions written for this lesson — not past exam papers.

Practise every question in this subject

  1. What does the signature on a JWT give you?

  2. Why is a token in localStorage a different risk from one in an HttpOnly cookie?

Try it