Forged tokens and stolen signing keys
A token nobody issued
The photo application's support console lets staff look up any customer's albums, reset sign-in methods, and close accounts. Every request to it carries a token signed by the photo service's sign-in service, auth.photos.example. The console checks the signature, the issuer, the audience, and the expiry, and if all four pass, it acts as the staff member the token names. It never asks how the token came to exist. It assumes that a person signed in, satisfied whatever the sign-in demanded, and that the sign-in service then wrote and signed this token.
A forged token breaks that assumption. It passes the console's checks, but no sign-in produced it. An attacker can get one in two ways: by obtaining the private key the sign-in service signs with, or by finding a validator that accepts tokens without real proof of that key. Either way, every protection that runs at sign-in is skipped. Passwords, passkeys, multi-factor authentication, and rules about devices and networks all apply to a sign-in, and a forger never goes through one.
Stolen sessions and replayed tokens, the subject of How sessions and tokens are stolen, start from a real sign-in by the victim. A forgery has no such starting point. That makes it powerful, and it also leaves a gap in the records that defenders can look for.
Stealing the key that signs
Storing and using keys called a token-signing key a key worth stealing, because whoever holds it can create a token saying they are anyone, and every application that trusts the issuer will believe it. With the photo service's key, an attacker can write a token for any staff member, choose its audience and roles, and set its expiry far in the future. SAML works the same way: a stolen assertion-signing key lets an attacker produce assertions that every service provider trusting that identity provider will accept.
Signing keys rarely leak through weaknesses in the cryptography. They leak from the places they are kept and used: a key file on a server, a backup or disk image that includes it, a source repository or build log, a crash dump or debugging snapshot taken while the key was in memory, or a test environment that was given a copy of the production key. An attacker with administrative access to the systems behind the sign-in service may simply read the key.
The damage grows with how widely the key is trusted. A key that signs tokens for one API exposes that API. A key that signs ID tokens and access tokens for every application, or that a provider hosting many organizations uses for all of them, exposes every one of them at once.
Validation that lets forgeries through
Some forgeries need no key at all, only a validator that does not insist on the right one. Decoding a token and reading its claims is not validating it, as Protecting credentials and messages pointed out, and Digital signatures set out the rule for validation itself: the verifier decides for itself which issuers, keys, and algorithms it accepts, and treats the token's header as a hint. Each common validation mistake breaks that rule in its own way.
- An unsigned token declares its algorithm as
noneand carries no signature. A validator that honors the declaration accepts claims anyone can write. - Algorithm confusion happens when the token chooses how it is checked. If the service signs with a private key, but a validator can be told that the token uses a shared-secret algorithm instead, it may check the token using the public key as the shared secret. The public key is public, so anyone can produce a matching value.
- A key supplied by the token, in a header field that embeds a public key or points to an address to fetch one from, lets the attacker sign with their own key and tell the validator to use it. The signature verifies, and proves only that whoever wrote the token signed it.
- Missing issuer and audience checks let a genuine token from somewhere else through: one issued for a different application, or by another issuer whose keys the validator happens to hold.
- Accepting another tenant's issuer is the multi-tenant form of the same mistake. Providers that host sign-in for many organizations often sign every tenant's tokens with the same keys, so a token from a tenant the attacker created for themselves verifies perfectly. Only the issuer, which names the tenant, shows that it does not come from the organization the application serves.
The fix for all of them is the same. Configure the validator with the issuer it expects, the audience that identifies this application, the algorithms it allows, and where to fetch the issuer's keys, and reject anything outside that configuration. Maintained libraries do this when they are given explicit settings rather than left to their defaults. Validating an ID token and Issuer, audience, and authorized party apply these checks to ID tokens.
Each token below has one flaw. Choose the check that rejects it.
Which check catches it?
Simulation. These decoded tokens are made up, and answering sends nothing. Each is sent to the photo application's business API, which expects the issuer https://idp.example.test/t-4821/, the audience https://business.photos.example, and an ES256 signature from a key in that provider's published key set.
Keeping signing keys out of reach
The strongest protection is a key nobody can copy. When the sign-in service's key is generated as non-exportable inside a hardware security module or a cloud key management service, as Storing and using keys describes, an attacker who takes over the sign-in server can request signatures while they have that access but cannot take the key away. Removing their access ends their ability to sign anything new. Tokens they signed in the meantime remain a problem until they expire or the key is replaced, but the incident has an end.
Separate keys for separate purposes limit what one leak exposes: different keys for ID tokens, access tokens, and SAML assertions, for each environment, and, at a provider serving many organizations, for each organization where that is practical. A development key should never be trusted in production.
Access to signing configuration deserves the same care as the keys. Few people and services need permission to manage, export, or replace a key, and every use of that permission should be recorded. Changes to the published key set, to the list of trusted issuers, or to the keys an application accepts are rare, so an alert on each one costs little. An attacker who cannot steal a key may try to add one of their own instead, which Abusing federation and account linking examines.
Noticing a token without a sign-in
Every genuine token has a history. A sign-in created a session, and the session led to the token, or a refresh or a client's own credentials did. The sign-in service records each of those steps, usually with the token's identifier (jti) or a session identifier (sid) that also appears in the token. A forged token has no history. Its identifier was never issued, or it names a session that never existed.
Finding that gap depends on the applications and APIs recording which tokens they accepted, by identifier rather than by value, and on someone comparing those records with the issuer's. The comparison can surface tokens with:
- an identifier the issuer has no record of issuing;
- a user who had no sign-in or refresh around the time the token says it was issued;
- a lifetime longer than the issuer ever grants, or an issue time in the future;
- a combination of audience, scopes, or roles the issuer never produces;
- more tokens carrying a key's identifier than the key service recorded signatures for.
Tokens for a support administrator that match no sign-in point to forgery: a stolen key, or a validator that accepted something it should have refused. That is a different problem from a stolen session, which always traces back to a real sign-in by someone. Innocent explanations exist, such as a gap in log collection or clocks that disagree, and they should be ruled out quickly, because if the suspicion holds, the response is drastic.
When a signing key is exposed
A planned key rotation overlaps the old and new keys so that nothing breaks, as Storing and using keys describes. An exposed key cannot wait for that overlap. The photo service generates a new key, publishes it, starts signing with it, and removes the exposed key from its published key set at once.
Removing the key ends trust in everything it ever signed, genuine and forged alike, because a signature cannot tell them apart. Staff and customers holding tokens signed with it are signed out or have to refresh. Applications that cached the old key set keep accepting forgeries until they fetch the new one, so they need to be told to refresh. Partners that configured a SAML signing certificate by hand have to install the new one before sign-in works for them again. An organization that already rotates keys routinely, and knows every verifier and partner that holds its public key, gets through this far faster than one doing it for the first time.
Replacing the key stops future forgeries. It does not undo what earlier ones did. The investigation uses the issuance records to find what the forged tokens were used for, then revokes the sessions and grants they created, reverses the changes they made, and finds out how the key escaped so that the new one does not follow it. Rotating credentials and keys after exposure follows that process in more depth.