AP-0112Identity & accessSeverity · High

A revoked session that keeps working

A user logs out, changes their password or is removed, and a token issued earlier keeps working. The product treats a valid signature as proof of a valid session, so a credential that should be dead is still accepted.

At a glance

Controls
OWASP API2:2023 · SOC 2 CC6.1 · ISO 27001 A.9.2
LikelihoodCommon where sessions are stateless tokens
Effort to exploitLow once a token is captured
Blast radiusThe full account, for the token’s lifetime
What review catchesLittle: logout looks like it works

Logging out is a promise: the credential you were using no longer works. Many products keep that promise only on the device that pressed the button. The token is self-contained and signed, the server trusts the signature, and nothing checks whether the session behind it was ended. On the screen the user is signed out; on the wire the old token still opens every door.

Why it happens

Stateless tokens are convenient because the server does not have to remember anything: it verifies a signature and reads the claims. That convenience is the weakness. Logout clears the token from the browser, but a copy taken earlier is still valid until it expires, which can be hours or days. Password change, account suspension and role downgrade have the same gap unless each one explicitly invalidates tokens already in the wild.

How the attack unfolds

An attacker who captured a token, through a logged request, a shared device, a compromised integration or a browser extension, keeps using it after the victim believes they are safe. The victim changes their password precisely because they suspect trouble, and the change does nothing to the attacker’s copy. The window is the token’s remaining lifetime, and during it the account behaves exactly as if the attacker were the owner.

A password change that leaves the attacker’s token alive is a lock changed on a door the attacker already walked through.MATT research team

Why review misses it

The logout code looks complete: it deletes the cookie and redirects. Nobody tests the token after logout, because the assumption is that logging out ends the session. The defect only appears when you keep a copy of the credential and try it again, which is a test written from the attacker’s point of view.

Where it hides

What a reviewer can look for in the code and in the behaviour, before anyone proves it.

  • Sessions are stateless tokens with no server-side record that can be marked revoked.
  • Logout clears the client cookie but makes no call that invalidates the token server-side.
  • A password change or role change does not rotate a signing secret or a per-user token version.
  • Token lifetimes are measured in hours or days, with no short-lived access token and refresh split.

A real instance

An account flagged for suspicious activity had its password reset by support. The attacker’s token, taken from a logged request days earlier, kept full access for another eleven hours until it expired on its own. Nothing the victim or support did shortened that window.

Details altered to protect the customer. Pattern, timing and outcome are as found.

How MATT tests it

The proof runs against the deployed product, on every release, whoever wrote the code.

  1. 01Capture a valid token, then log out and replay the token against protected endpoints.
  2. 02Change the account’s password and replay the pre-change token.
  3. 03Downgrade or suspend the account and confirm the old token loses the removed privileges within the promised window.
  4. 04Measure how long a revoked credential keeps working, and check the same across every device and API client.

The evidence

Before the fix
  1. GET /api/me (after logout)200 OKPre-logout token still returns the full profile
  2. GET /api/me (after password change)200 OKOld token unaffected by the reset
After the fix
  1. GET /api/me (after logout)401 UnauthorizedSession marked revoked server-side, token refused
  2. GET /api/me (after password change)401 UnauthorizedPassword change rotates the per-user token version
The rule, in plain English

A revoked session must stop working everywhere within one minute.

Executable check

After logout, password change and account removal, a captured token must be refused by every protected endpoint within one minute, on every release.

Every rule MATT holds →

What the fix looks like

  1. 01

    Keep a revocation record

    Track sessions server-side, or carry a per-user token version that a password or role change increments, so a single event ends every credential at once.

  2. 02

    Split access from refresh

    Issue short-lived access tokens against a revocable refresh token, so the longest a dead session can survive is minutes.

  3. 03

    Prove the window

    A revoked session must stop working everywhere within one minute, checked on every release.

A check that was never written is found by proving the deployed behaviour, fixed with the engineer who shipped it, and kept fixed by running the control on every change. What is ExploitOps? →

ExploitOps discovery call

Understand where your security loop breaks.

A 15-minute call with a MATT co-founder to understand your product and whether an ExploitOps Review is worth doing. If there is a real need, we scope one with the research team.