ASVS 5.0 V7.4.2 (Level 1) requires that when a user account is disabled or deleted, for example because an employee left, every active session for that account ends. Disabling a record in the database is not enough if tokens keep working.
Why it matters
Offboarding is when risk peaks. A departing employee with a valid session can keep reading data until the idle timeout, and an attacker using a stolen session on a disabled account may never be cut off if sessions are only checked at login.
How to implement it
Make session validation check the account state, and revoke sessions as part of the disable action. Doing both gives defense in depth.
def disable_user(db, user_id):
with db.transaction():
db.execute("UPDATE users SET disabled = 1 WHERE id = ?", (user_id,))
db.execute("UPDATE sessions SET revoked = 1 WHERE user_id = ?", (user_id,))
db.execute("UPDATE refresh_tokens SET revoked = 1 WHERE user_id = ?", (user_id,))
def load_session(db, token_hash):
row = db.execute(
"SELECT s.user_id FROM sessions s JOIN users u ON u.id = s.user_id "
"WHERE s.token_hash = ? AND s.revoked = 0 AND u.disabled = 0",
(token_hash,),
).fetchone()
return row
For self contained tokens, keep a per user "not before" timestamp and reject tokens issued earlier (see V7.4.1). Do not forget API keys, refresh tokens and long running websocket connections. If you use SSO, disabling at the identity provider must also trigger logout at your app, for example through back channel logout.
How to test it
- Log in as a test user in two browsers.
- Disable the account from the admin tool.
- Make a request from each browser. Both must be rejected on the very next request.
- Repeat for deletion and for API token use.
Common mistakes
- Revoking only the current browser session.
- Forgetting refresh tokens, so the user quietly gets a new access token.
- Relying on the next login attempt to notice the change.
Quick check
Is a short idle timeout a substitute?
No. The requirement is that sessions end when the account is disabled, not eventually.