ZITADEL: Cross-Tenant User Leakage via Recycled Identifiers
Description
Summary
A flaw in the user lifecycle enforcement allowed deleted users to retain their original organization/tenant association. Recreating a deleted user under a distinct organization can cause the new user instance to be incorrectly provisioned within the original organization if the previous ID would be used to recreate it.
Impact
When a user is created, the system maps the generated or provided ID to its target organization (Org A). When that user is subsequently deleted, a deletion event is appended to the stream, but the historical mapping of the resource owner within the event store's validation layer is not cleared.
If a new user is later provisioned in a different organization (Org B) using that exact same ID, the event store validation logic reads the stream's history, matches it to the original organization, and routes the new user's events to Org A instead of Org B.
This issue represents a localized multi-tenancy isolation anomaly rather than an easily exploitable attack vector. Because the new user instance is incorrectly routed and provisioned inside Org A instead of Org B, an administrator from Org A inadvertently gains full access to this new user record.
However, there is no technical mechanism for a malicious actor to force, automate, or target this behavior against a specific user or tenant. Because the scenario relies entirely on an accidental sequence of operational events and requires the recycling of a highly specific ID space, the practical security risk is exceptionally low.
Affected
Versions
Systems running one of the following versions are affected:
- 4.x:
4.0.0through4.15.1(including RC versions) - 3.x:
3.0.0through3.4.11(including RC versions)
Patches
The vulnerability has been addressed in the latest releases. The patch resolves the issue by requiring the correct permission in case the verification flag is provided and only allows self-management of the email address, resp. phone number itself.
Workarounds
The recommended solution is to upgrade to a patched version.
Questions
If you have any questions or comments about this advisory, please email us at security@zitadel.com
Credits
Thanks to Charlie Graven from Famedly for reporting this vulnerability.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Affected products
1Patches
Vulnerability mechanics
Root cause
"The eventstore's `commands_to_events` function always inherited the owner from the most recent event in the stream, so when a deleted user's aggregate ID was reused in a different organization, the new user was incorrectly routed to the original organization."
Attack vector
An attacker cannot directly trigger this bug; it requires an accidental operational sequence where a user is deleted and later a new user is created with the exact same aggregate ID in a different organization. The eventstore's validation logic reads the stream history and, because the old owner mapping is not cleared, routes the new user's events to the original organization instead of the intended one. This results in a multi-tenancy isolation anomaly where an administrator from the original organization gains access to the new user record. The advisory notes there is no technical mechanism for a malicious actor to force, automate, or target this behavior [ref_id=1].
Affected code
The vulnerability resides in the eventstore's `commands_to_events` and `push` functions in `cmd/initialise/sql/08_events_table.sql`, `cmd/setup/40/02_func.sql`, `cmd/setup/64.sql`, and `cmd/setup/70.sql`, as well as the Go wiring in `internal/eventstore/v3/eventstore.go`. The old `eventstore.command` type lacked an `enforce_owner` flag, causing the owner to always be inherited from the most recent event in the stream even when a new aggregate with the same ID was being created under a different owner.
What the fix does
The patch introduces a new composite type `eventstore.command2` with an `enforce_owner` boolean field and rewrites the `commands_to_events` function [patch_id=6467409]. When `enforce_owner` is true, the command's owner is written directly; when false, the existing aggregate owner from the stream is retained. This allows create-like events for reused aggregate IDs to explicitly set a new owner, preventing the historical owner mapping from being incorrectly inherited. The Go code in `internal/eventstore/v3/eventstore.go` registers the new type and wires the `EnforceResourceOwnerCommand` interface so command types can opt into enforced owner behavior.
Preconditions
- configA user must be deleted, leaving a historical event stream with the original organization owner mapping.
- inputA new user must be created with the exact same aggregate ID in a different organization.
- configThe system must be running an affected version (4.0.0 through 4.15.1 or 3.0.0 through 3.4.11).
Generated on Jun 18, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.