ZITADEL is an open-source identity infrastructure software that allows organizations to manage their user accounts, authentication, and authorization. Its self-contained server provides APIs and UI for managing users and their permissions within an organization.

A security vulnerability has been discovered in ZITADEL versions prior to 2.71.6, 2.70.8, 2.69.9, 2.68.9, 2.67.13, 2.66.16, 2.65.7, 2.64.6, and 2.63.9. The vulnerability, classified as CVE-2025-31123, allows an attacker with an expired JWT key to obtain valid access tokens due to improper validation of the expiration date of the JWT key when used for Authorization Grants. This issue does not affect the JWT Profile for OAuth 2. Client Authentication on the Token and Introspection endpoints, which correctly reject expired keys.

作者Github_Record_label
'])

Vulnerability Details

When an application uses ZITADEL for user authentication, it relies on the JWT authorization grants to give users permission to access the application. As a part of the process, ZITADEL generates JWT keys with a specific expiration time, which should prevent the keys from being used after they have expired. However, due to the vulnerability in ZITADEL, the application fails to check the expiration date of the JWT key properly, allowing an attacker with an expired key to obtain valid access tokens.

Code Snippet Showcasing the Issue

Before the fix, JWT token expiration check was missing in the code, allowing attackers to bypass the expiration check:

app.post("/oauth2/token", (req, res) => {
  const authHeader = req.headers.authorization;
  const jwtKey = getJwtKeyFromHeader(authHeader);
  
  // Check if JWT key is valid
  if (isValidJwt(jwtKey)) {
    // Create a new access token and send it in the response
    const accessToken = createAccessToken(jwtKey);
    res.json({ access_token: accessToken });
  } else {
    res.status(401).send("Unauthorized");
  }
});

With the fix applied, the code checks for JWT token expiration properly

app.post("/oauth2/token", (req, res) => {
  const authHeader = req.headers.authorization;
  const jwtKey = getJwtKeyFromHeader(authHeader);
  
  // Check if JWT key is valid and not expired
  if (isValidJwt(jwtKey) && !isJwtExpired(jwtKey)) {
    // Create a new access token and send it in the response
    const accessToken = createAccessToken(jwtKey);
    res.json({ access_token: accessToken });
  } else {
    res.status(401).send("Unauthorized");
  }
});

Solution and Patch

The vulnerability has been fixed in ZITADEL versions 2.71.6, 2.70.8, 2.69.9, 2.68.9, 2.67.13, 2.66.16, 2.65.7, 2.64.6, and 2.63.9. It is highly recommended to update your ZITADEL installation to the latest version to prevent potential security risks. If you are using an older version, please consult the official GitHub repository for instructions on how to update your ZITADEL instance safely.

Additional Resources

- ZITADEL GitHub Repository
- CVE-2025-31123 Vulnerability Details
- ZITADEL Documentation
- JWT Profile for OAuth 2. Client Authentication

Conclusion

CVE-2025-31123 is a high-risk vulnerability that could lead to unauthorized access to protected resources by an attacker using expired JWT keys. It's crucial for organizations using ZITADEL to update their identity infrastructure software to the latest patched version to mitigate this risk.

Timeline

Published on: 03/31/2025 20:15:15 UTC
Last modified on: 04/01/2025 20:26:22 UTC