Security News

Cybersecurity news aggregator

🔓
MEDIUM Vulnerabilities Reddit r/netsec

Source-reviewing 200+ self-hosted multi-tenant AI/SaaS apps for tenant isolation: 78 leaked across tenants (the "un-retrofitted read sibling")

  • What: 78 multi-tenant AI/SaaS apps have cross-tenant data exposure flaws
  • Impact: Sensitive data from one tenant may be accessed by another
Read Full Article →

Security research I source-reviewed 200+ self-hosted AI tools for tenant isolation. 78 leaked across tenants. The same isolation flaw, the un-retrofitted read sibling, in 78 of 200+ multi-tenant AI and SaaS products. The pattern, the fixes that shipped, and how to check your own. Every team building on top of LLMs eventually ships the same feature: workspaces. Tenants, teams, projects, organizations, whatever you call them, the promise is identical. Your knowledge base, your chat history, your documents, walled off from everyone else on the same instance. That wall is the one thing a multi-tenant product cannot get wrong. So over the past few months I went looking, systematically, for where it cracks. I source-reviewed 200+ multi-tenant AI and SaaS products for one specific class of bug, and confirmed cross-tenant data exposure in 78 of them: one tenant able to read (and in some cases modify or delete) another tenant's data. That comes to 84 findings across 78 products, with 31 now filed as GitHub Security Advisories. Almost all of them are the same mistake, and it lives in the read endpoints. This post explains that mistake, names the ones that are already fixed, and points to the live list of the rest. Everything still unnamed stays that way until its fix ships. I'm not going to hand attackers a live target before users are protected. How I tested Two tiers, because 200+ deep labs isn't something one person does, but 200+ source reviews is. The sweep (200+ products). For each candidate I read the code for one pattern: an authorization check that's enforced on the write path but skipped on a neighboring read. Most products either don't have the bug, aren't actually multi-tenant in their open-source edition, or have already retrofitted every sibling. Those are the majority, and they're the denominator that makes the 78 mean something. The confirmation (the findings). When the source review flagged a real gap, I confirmed it for real: Stood up a clean, self-hosted instance (Docker, on my own hardware). Created two tenants, A and B, with synthetic accounts and synthetic canary data. No real users, no production systems, ever. Reproduced the cross-tenant read, modify, or delete with Sectum AI , an open-source multi-tenant isolation verifier, and confirmed it against database ground truth. Captured a signed (RFC-3161-timestamped) evidence pack and reported it privately to the maintainer under coordinated disclosure (90-day hold), usually with the fix attached. That last step is why the scoreboard below is mostly blank. Most of the 84 findings are still under coordinated disclosure. The maintainers have been told and are shipping fixes, so I name only the ones that are already public and aggregate the rest. The full current list, and each advisory as it publishes, lives at sectum.ai/research . The pattern: writes are guarded, reads are not What made the sweep almost mechanical is that the same shape kept repeating. The write path was correctly scoped to the tenant, and the read path right next to it was not. A developer adds "delete this item" and remembers to check ownership: does this item belong to the caller's tenant? Good. Then someone adds "view this item" or "list its embeddings," and the check never gets copied over. The list/get/search endpoint fetches by a raw ID with no tenant filter. The IDs are often sequential integers or otherwise guessable, so "view item 5" happily returns another tenant's item 5. I started calling these un-retrofitted read siblings. Once you've seen one, you grep for the rest: find the endpoint that checks ownership, then look at the neighbors that don't. Across 78 products it was the same shape almost every time. This isn't 84 unrelated bugs. It's one bug, 84 times. There's a second, nastier variant. The check runs, but on the wrong thing. The handler authorizes the path you supplied rather than the object you asked for. You pass your own workspace's ID, which you're allowed to touch, along with a victim's object ID, and the permission check and the object lookup disagree about whose data this is. The two most damaging classes I confirmed were an embedding-vector read (raw RAG vectors, which can partially reconstruct the source text) and a connector re-index that exfiltrates another tenant's content using the victim's own stored OAuth token. The ones already fixed Five are public because the maintainers shipped a fix. The teams that moved fastest deserve the credit: Tool Issue (class) Status SurfSense Cross-tenant connector re-index to exfiltrate another tenant's GitHub/Notion content using their stored credentials Fixed: PR #1503 merged AnythingLLM Embed widget exposed to anonymous origins by default Fixed: CVE-2025-63390, PR #5759 merged Baserow Cross-workspace disclosure of a field's values by serial ID Fixed and deployed: PR #5613 aideepin Cross-user read of another user's RAG chunk text, embedding vectors, and document text by ID Fixed: PR #105 merged Flagsmith Cross-proje...

Share this article