GHSA-258c-965c-p3hc Daptin's Session Management Vulnerability Leads to Insufficient Session Expiration After Password Change
SummaryA session invalidation vulnerability exists in daptin's authentication system where JSON Web Tokens (JWTs) remain fully valid after a user changes their password. The JWT validation middleware (`CheckJWT`) only verifies token signature, expiry, issuer, and signing algorithm — it does not check whether the token was issued before the most recent password change. The password update code path hashes the new password but never calls `InvalidateAuthCacheForEmail()` and never revokes or blacklists existing tokens. This effectively negating password rotation as an incident response control.Vulnerable Files`daptin/server/jwt/jwtmiddleware.go` — JWT validation without session versioning`daptin/server/resource/resource_update.go` — password update without session invalidation`daptin/server/actions/action_generate_jwt_token.go` — JWT claims lack password version`daptin/server/auth/auth.go` — `InvalidateAuthCacheForEmail` exists but not called on update`daptin/server/resource/columns.go` — password change action wiringVulnerable Code Snippet**1. JWT validation checks nothing beyond signature/expiry/issuer (jwtmiddleware.go:232-260):****No check exists for password version, session version, or token revocation status.** A token issued before a password change passes all these checks identically.**2. Password update hashes new password but never invalidates sessions (resource_update.go:282-287):**The new bcrypt hash is stored, but no call to `auth.InvalidateAuthCacheForEmail()` is made, and no token revocation mechanism is triggered.**3. JWT claims lack any password-bound claim (action_generate_jwt_token.go:74-83):**Claims include `email`, `sub`, `name`, `nbf`, `exp`, `iss`, `iat`, `jti` — but **no `pwd_version`** or equivalent claim that could be compared against a server-side value during validation.**4. InvalidateAuthCacheForEmail exists but is NOT called on password update (auth.go:520-530):**This function is called in `resource_create.go:470`, `resource_delete.go:73`, and `dbmethods.go:1194` — but **never** in `resource_update.go`, which is the code path for password changes.PoC (Proof of Concept)Manual Exploitation StepsCreate a user account on the daptin instanceSign in and capture the JWT (this becomes the "stolen" token)Change the user's password via `PATCH /api/user_account/{id}`Reuse the original JWT — it still returns `HTTP 200` with full data accessConfirm the old password no longer works for new login (password did change)Confirm the old token still allows write operations (full CRUD retained)**Result: `HTTP 200`** — write operations also succeed with the old token.Automation PoC**Verified test output:**Recommended Fix**Add a `password_version` column** to the `user_account` table (integer, incremented on each password change)**Include `pwd_version` in JWT claims** at token generation time (`action_generate_jwt_token.go:74`)**Check `pwd_version` during validation** in `CheckJWT()` (`jwtmiddleware.go:232-260`) — compare the claim value against the current database value; reject if mismatched**Call `InvalidateAuthCacheForEmail()`** in `resource_update.go` when a password column is updated, to force the auth cache to re-fetch user state
CVSS v3.1 score: 6.5