
Inspect an EXE without running it: reading PE headers
What the PE headers, section table, import list and per-section entropy tell you about a Windows executable, and where static triage stops being enough.

A JWT is signed, not encrypted: anyone holding it can read the payload. And until it expires, a token is a live credential that works exactly like the password behind it.
A JWT in compact serialisation is three dot-separated parts: header.payload.signature. The first two are JSON encoded with base64url; the third is a signature over the first two. The encoding detail matters more than people expect. It is base64url, not base64: - and _ replace + and /, and the trailing = padding is stripped. That is what lets a token ride in a URL or a header without escaping, and it is why a generic base64 decoder sometimes chokes on a perfectly valid token.
The signature covers exactly the string base64url(header) + "." + base64url(payload). Change one character anywhere in it and the signature no longer matches. The consequence is the part people forget: a JWT is protected against tampering, not against reading.
Decoding a token is base64url in reverse. It needs no key, touches no signature, and checks no clock. A decoder will happily render a token that was signed with the wrong key, expired last March, or was issued for a different service entirely. Verifying means recomputing the signature with a key you trust, confirming the algorithm is the one you expected, and checking the time and audience claims.
A complete verification is four questions, and skipping any one of them leaves a token that looks fine and is not safe: does the signature check out against the right key; is alg on the server's allow-list; do iss and aud match what this service expects; are exp and nbf satisfied right now.
Two header fields do most of the work. alg names the signing algorithm and kid identifies the key. Both arrive inside the token, which means both are supplied by whoever sent it. Trusting them without question is the root of the classic JWT failures.
alg to none and leave the signature segment empty. A verifier that takes its algorithm from the token will accept it without checking anything. Pinning the accepted algorithms server-side is the fix.kid is concatenated into a file path or a SQL query, it becomes path traversal or injection. Resolve it only against a known set of keys.jwk and jku headers embed a key or a URL to fetch one. Unless they are disabled or tightly allow-listed, you have built a token that verifies itself.A JWT is signed, not encrypted. Anyone who sees the token can read the payload: a browser extension, a forward proxy, an access log, an error-tracking service, the support ticket someone pasted it into. JWE exists for encrypted tokens, but session tokens in the wild are overwhelmingly plain signed JWS. So the contents of the payload are a privacy decision, not just a schema decision.
The registered claims are a short list and each one has a job: iss who minted it, sub who it is about, aud who is meant to accept it, exp when it stops being valid, nbf when it starts, iat when it was issued, and jti a unique id that lets you detect replay or revoke a single token. For jti you want collision resistance rather than sequence, which is what the UUID and ULID generator is for.
Size is a practical constraint too. The token travels on every request, and a permissions array that grows with the product will eventually meet a reverse proxy with a header size limit of a few kilobytes. Choosing between embedding authorisation in the token and looking it up server-side is also a choice about whether you can revoke it.
exp, nbf and iat are Unix timestamps in seconds, not milliseconds. A surprising share of "this token expired immediately" bugs is that unit, usually introduced by a JavaScript service passing Date.now() straight through. To read the raw values while debugging, the timestamp converter turns them into dates you can compare against your logs.
Clocks drift. A few seconds of difference between the issuer and the verifier will reject tokens at the edge of their window for no good reason, so verifiers allow a small leeway. Thirty seconds is generous; a leeway measured in minutes quietly cancels out the short lifetime you chose.
Revocation is the harder problem. A signed token is valid on its own terms until it expires. Delete the user, change their role, disable the account: the token already issued keeps working. The usual answer is short-lived access tokens measured in minutes, a refresh token stored server-side where it can be revoked, and a jti deny-list for the emergencies where minutes are too long.

An unexpired token is a live credential. Whoever sees it can act as that user until it expires. Pasting a production token into an unknown online decoder is the same class of mistake as pasting a password.
Pick a tool that satisfies those constraints. The JWT decoder and verifier decodes in the browser and verifies signatures through the browser's WebCrypto implementation, so the token itself is not sent anywhere; the only outbound request happens if you point it at a JWKS URL yourself. For a long payload with nested claims, the JSON viewer is easier to read than a wrapped text box.
none is never on the list.iss and aud, otherwise a genuine token minted for another service will pass here too.exp and nbf with a small, deliberate leeway.kid only within that key set, and plan rotation before you need it.The same discipline applies next door. If you pulled an Authorization header out of a network capture, the handling rules in reading a pcap apply to the capture file as well as the token. And the gap between decoding and verifying has a direct analogue in inspecting an EXE, where reading a header is not the same as clearing the file.
No. The tokens you meet day to day are signed JWS: protected against tampering, not against reading. Anyone can base64url-decode the payload. JWE exists for encrypted tokens but is rarely used for ordinary session tokens.
Not on its own. Decoding ignores the signature, the algorithm and the clock. Validity means the signature verifies against a trusted key, alg is on your allow-list, iss and aud match this service, and exp and nbf are satisfied.
The specification defines an unsecured JWS where alg is none and the signature segment is empty. A verifier that takes the algorithm from the token can accept such a token without checking a signature at all. Pinning accepted algorithms server-side prevents it.
Expired tokens are already rejected. The problem is the unexpired ones: a signed token stays valid on its own terms even after you delete the account. Keep access tokens to a few minutes, hold refresh tokens server-side, and maintain a jti deny-list for emergencies.
No. An unexpired token lets the holder act as that user. Use a short-lived staging token instead, never enter a signing secret into a web form, and if you had to use a real token, end the session afterwards.

What the PE headers, section table, import list and per-section entropy tell you about a Windows executable, and where static triage stops being enough.

How pcap and pcapng are laid out, where snaplen truncation bites, how to turn packets into flows and endpoints, and what TLS still leaks in a capture.