Feb 28, 2026·6 min read·4 visits
ZITADEL fails to verify the full integrity of decrypted V2 opaque access tokens. By truncating a valid token to approximately 80 characters, an attacker can bypass integrity checks, as the system validates the session identifier but ignores the missing user ID suffix. Fixed in versions 3.4.7 and 4.11.0.
A logical integrity vulnerability exists in ZITADEL's handling of V2 opaque access tokens. Due to insufficient validation of the decrypted token payload, the system accepts truncated tokens that lack the required user identity suffix. This flaw allows modified authentication artifacts to pass verification checks, violating the integrity of the authorization mechanism.
ZITADEL, an open-source identity management platform, utilizes opaque access tokens for user authorization. In affected versions, a vulnerability exists in the validation logic for "V2" format tokens. These tokens are encrypted strings that, when decrypted, contain a composite structure: a token identifier (session reference) and a user identifier. The vulnerability arises from the system's failure to enforce strict integrity checks on the entire decrypted payload.
The specific flaw allows a token to be truncated—stripping the user identifier portion of the payload—while remaining valid in the eyes of the verification logic. Because the system successfully extracts the token identifier from the prefix and retrieves the corresponding session from the database, it erroneously assumes the token is valid without verifying that the token's embedded user identity matches the database record. This represents a violation of CWE-302 (Authentication Bypass by Assumed-Immutable Data).
The root cause lies in the implementation of the verifyAccessTokenV2 function within the event sourcing repository. ZITADEL V2 tokens encapsulate two pieces of data in their decrypted plaintext: a token_id (formatted as v2_<oidc_session>...) and a user_id.
When a request is received, the system performs the following steps:
token_id from the beginning of the plaintext.token_id to retrieve the active session and associated metadata (including the authoritative user_id).In vulnerable versions, the verification logic stopped there. It implicitly trusted that if the token_id existed and was active in the database, the token was valid. It failed to compare the user_id present in the token's tail against the user_id retrieved from the database. Consequently, if a token was truncated such that the token_id remained intact but the user_id was removed, the system processed the request as authenticated, ignoring the data discrepancy.
The vulnerability exists in internal/authz/repository/eventsourcing/eventstore/token_verifier.go. The fix introduces a mandatory parity check between the token's claim and the database's record.
Below is the logic flow before and after the patch:
// VULNERABLE LOGIC (Conceptual)
// The system retrieves the token object based on the ID found in the input string.
activeToken, err := repo.eventstore.GetToken(ctx, tokenID)
if err != nil {
return error
}
// The function returns success here without checking if the input 'subject' (userID from token)
// actually matches 'activeToken.UserID' (userID from DB).
return activeToken.UserID, nilThe patch enforces strict equality. If the decrypted subject does not match the database record, the token is rejected as invalid or tampered with.
// PATCHED LOGIC in verifyAccessTokenV2
func (repo *TokenVerifierRepo) verifyAccessTokenV2(ctx context.Context, token, subject, ...) {
// ... [Retrieval of activeToken from database] ...
// CRITICAL FIX: Verify the subject (decrypted UserID) matches the DB record.
// If the token was truncated, 'subject' would be empty or malformed.
if activeToken.UserID != subject {
// Explicitly reject the request if the identities do not align
return "", "", "", "", "", zerrors.ThrowUnauthenticated(nil, "APP-3f4fs", "invalid token")
}
// ... [Continue with expiration checks] ...
}The exploitation of this vulnerability is trivial but requires access to a valid V2 token. The attack vector is network-based and involves token malleability.
token_id prefix but discards the suffix containing the user_id.Authorization: Bearer <token> header of an API request.The ZITADEL backend decrypts the partial ciphertext (or processes the prefix depending on the encryption mode specifics), successfully extracts the session ID, and validates the request. The server responds with 200 OK despite the token being structurally corrupt. While this does not inherently allow an attacker to generate tokens for other users (as the token_id contains high-entropy random values), it degrades the integrity of the authentication protocol by allowing malformed credentials to be accepted.
The severity of this vulnerability is classified as Medium (CVSS 4.3). The impact is primarily on the integrity of the authentication mechanism rather than direct confidentiality or availability loss.
token_id which acts as the database key. The attack is limited to modifying existing valid tokens.CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Remediation requires updating the ZITADEL instance to a patched version that includes the logic to cross-verify the token subject against the database record.
No configuration changes are required after the upgrade. The fix is internal to the TokenVerifierRepo logic. Existing tokens remain valid; however, any truncated tokens cached or stored by malicious actors will immediately cease to function, resulting in Unauthenticated errors (APP-3f4fs).
Security teams can identify exploitation attempts by analyzing application logs for specific error patterns post-patch. Look for increased occurrences of the error ID APP-3f4fs with the message "invalid token," which indicates that a token's decrypted subject did not match the associated session owner.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
ZITADEL ZITADEL | >= 3.0.0, < 3.4.7 | 3.4.7 |
ZITADEL ZITADEL | >= 4.0.0, < 4.11.0 | 4.11.0 |
| Attribute | Detail |
|---|---|
| CWE | CWE-302 |
| CVSS v3.1 | 4.3 (Medium) |
| Attack Vector | Network |
| Impact | Integrity Loss |
| Exploit Status | PoC Available |
| EPSS Score | 0.015% |
Authentication Bypass by Assumed-Immutable Data