Why your RAG index can read documents the user cannot
Ask the team who built your document assistant a single question: when the assistant searched, whose permissions were applied? The answer is frequently a pause, followed by the observation that the index was built by a service account with read access to the whole document store — and that nothing since has narrowed it back down.
Published 22 September 2026. Written by the SecHB practice, Greater Vancouver, British Columbia.
The short version
Retrieval-augmented generation works by copying your documents into an index and searching that index. The copy is made by whatever principal ran the ingestion, and that principal is usually granted read access to everything so that ingestion does not fail halfway through. The index therefore represents the union of everything that account could see.
When a user asks a question, the search runs against that union. Unless something explicitly re-applies that user’s permissions, retrieval returns results scoped to the ingestion account, not to the person asking. The source system’s access control is still intact and still correct; it is simply no longer in the path. That is the whole mechanism, and it is worth checking in your own system before reading further.
Why it is almost never noticed at build time
Nothing about it looks like a bug. The pilot is run by people who are allowed to see the pilot corpus, so every answer they get is an answer they were entitled to. Retrieval quality testing optimizes for finding the right document, which is the opposite of the behaviour you would need to notice. There is no error, no denial, no exception — a permission failure in a normal application produces a 403 somebody sees, while here it produces a helpful paragraph.
The demo also works perfectly, which is the most misleading signal of all, and the team that built it is not the team that owns the source system’s permission model. By the time the assistant is answering for everyone, the gap is a property of the architecture rather than a defect anyone introduced.
The OWASP Top 10 for LLM Applications 2025 covers this ground as LLM08:2025 Vector and Embedding Weaknesses, with the consequence landing as LLM02:2025 Sensitive Information Disclosure. That entry names the mechanism directly: Inadequate or misaligned access controls can lead to unauthorized access to embeddings containing sensitive information
, and, for shared stores, In multi-tenant environments where multiple classes of users or applications share the same vector database, there’s a risk of context leakage between users or queries.
Its first mitigation is to Implement fine-grained access controls and permission-aware vector and embedding stores
— which is what the rest of this page is about building.
Three designs, compared
| Design | How it works | What it costs you |
|---|---|---|
| One index per tenant | Physical separation. The query cannot reach another tenant’s vectors because they are not in the store being queried. | Strongest and simplest to reason about. Costly at high tenant counts, and it does nothing for boundaries inside a tenant. |
| Filtered retrieval with enforced metadata | One index; every chunk carries permission metadata; the filter is applied by the retrieval service from the session, never from the request body. | Scales well and is the common choice. Fails open in several specific ways, below. |
| Post-retrieval permission checks | Retrieve broadly, then discard what the user may not see before anything reaches the model. | Useful as a second layer and as a detector. Weak alone: the data left the store, ranking already leaked signal, and one path that forgets the check exposes everything. |
No. An instruction in a prompt is a request, not an access control. The retrieval has already happened by the time the model reads anything, the content is already in the context window, and a model that has been told to ignore a document has still been given it. Filtering must happen before or during retrieval, enforced by code the model cannot address.
Where metadata filtering fails open
Filtered retrieval is the design most teams land on, and it is a reasonable choice. These are the specific ways it stops working, each of which is worth checking by hand rather than assuming.
| Failure | What it looks like |
|---|---|
| Missing metadata treated as permitted | A chunk ingested before the field existed, or by a pipeline that did not set it, matches a filter that only excludes known-forbidden values. |
| The filter taken from the request | A tenant or group identifier read from a parameter the caller controls rather than from the authenticated session. |
| One unfiltered path | A summarization job, an evaluation harness, a support tool or a scheduled digest that queries the store directly with service credentials. |
| Permissions frozen at ingestion | Access was revoked in the source system; the copy in the index was never updated. |
| Leakage around the content | Titles, filenames, citations, snippet previews or "no results" versus "results you cannot see" disclosing what exists. |
The common thread is direction. A filter that excludes what is known to be forbidden fails open on anything unknown; a filter that includes only what is known to be permitted fails closed. Default-deny costs a little retrieval quality on badly labelled corpora and is the correct trade.
Chunk-level access control, and keeping it in step
Permissions in source systems are rarely uniform across a document. A spreadsheet has restricted tabs; a wiki page has a section only one group should read; an export bundles records with different handling rules into one file. Chunking splits documents into passages, and the passage is what gets retrieved — so access control has to survive the split.
Doing it properly means resolving the effective permission for each chunk at ingestion, storing it in a form the retrieval filter can evaluate, and handling the awkward cases: inherited folder permissions, group membership that is itself dynamic, links shared outside the group model. It is real work, and it is the part most often deferred and then never done.
Keeping it current is the other half. Only if something propagates the change. Permissions live in the source system; the index holds a copy taken at ingestion. Unless revocation triggers re-indexing or the filter is evaluated against live permissions at query time, the index keeps answering from the world as it was. Whichever way you go, the staleness window is a number the organization should choose deliberately rather than discover during an incident: how long may the index keep answering from permissions that have changed?
Embedding leakage and inversion, briefly
A related but distinct problem: embeddings are not a form of protection. The OWASP entry lists embedding inversion among the risks in plain terms — Attackers can exploit vulnerabilities to invert embeddings and recover significant amounts of source information, compromising data confidentiality.
Treat the vector store as holding the sensitivity of the text it was built from — same encryption, same access control, same retention thinking, same care about who can dump it and where backups go.
It is a different problem from the one above, with the same data at the centre, and the practical instruction is short: a vector store is a copy of your documents, so protect it like one.
Testing it properly
A smoke test asks whether the assistant returns good answers. An isolation test asks whether it returns answers it should not, which requires you to try to make it do so.
Create two accounts that should see different things, put a distinctive marker in a document only the first may read, then ask the second account questions designed to surface it — by topic, by paraphrase, by asking for a summary of everything on the subject. Do it through the assistant, not against the vector store, because the assistant is the path a real user has. Beyond that first pass, a real test covers the paths the happy path does not: the unfiltered background job, the export or citation feature, the behaviour immediately after a permission change, ingestion of a document with missing metadata, and whether a caller can influence the filter by manipulating the request. It also probes indirectly — asking for summaries, counts, comparisons and "is there anything about X", because those surface content the retrieval layer saw even when the assistant declines to quote it.
Where that work is carried out by someone outside the organization, scope is agreed in writing first and testing proceeds only with signed authorization from someone entitled to give it — which, for a multi-tenant system holding other organizations’ documents, is a question worth settling before anyone begins.
A short checklist
- Which principal built the index, and what can it read?
- Whose permissions are applied at query time, and where in the code?
- Is the filter derived from the authenticated session or from the request?
- Does a chunk with no permission metadata get returned or excluded?
- Which jobs or tools query the store without a user context?
- How long after a revocation can the index still answer from it?
- Do titles, citations or "no results" messages disclose existence?
- Has anyone tried to retrieve across the boundary on purpose?
Yes, because "tenant" is the wrong word for the boundary that usually matters. Inside a single organization the same failure separates departments, seniority levels and jurisdictions: the board pack, the severance agreement, the file one team holds under a contractual restriction. A single-tenant deployment removes the cross-customer version of the problem and leaves the internal one untouched.
How the work is bounded
The scope is agreed in writing before work starts, and the engagement is quoted in writing with it.
Testing and adversary simulation are carried out only with signed authorization, to a scope agreed in writing.
SecHB does not issue certifications, attestations or audit opinions: those come from accredited certification bodies, CPA firms and QSAs. The work here is what an organization does to be ready for them.
Nothing here is legal advice. Where a question turns on the law, the work is done alongside the client’s counsel, not instead of them.
Questions we are asked
Does this matter with one tenant?
Yes, because "tenant" is the wrong word for the boundary that usually matters. Inside a single organization the same failure separates departments, seniority levels and jurisdictions: the board pack, the severance agreement, the file one team holds under a contractual restriction. A single-tenant deployment removes the cross-customer version of the problem and leaves the internal one untouched.
Can we just filter in the prompt?
No. An instruction in a prompt is a request, not an access control. The retrieval has already happened by the time the model reads anything, the content is already in the context window, and a model that has been told to ignore a document has still been given it. Filtering must happen before or during retrieval, enforced by code the model cannot address.
How do we test this ourselves?
Create two accounts that should see different things, put a distinctive marker in a document only the first may read, then ask the second account questions designed to surface it — by topic, by paraphrase, by asking for a summary of everything on the subject. Do it through the assistant, not against the vector store, because the assistant is the path a real user has.
If we revoke someone’s access, does the index follow?
Only if something propagates the change. Permissions live in the source system; the index holds a copy taken at ingestion. Unless revocation triggers re-indexing or the filter is evaluated against live permissions at query time, the index keeps answering from the world as it was.
Where to go from here
The broader design of retrieval and vector storage is RAG and vector security, and the surrounding architecture — broker placement, credentials, egress — is a secure AI environment. To have someone attempt the crossing deliberately, LLM application penetration testing is that engagement; to map the system before testing it, an AI security assessment comes first. Other pieces are indexed under Writing.
Tell us which account built the index and whose permissions are applied at query time, and the reply will say whether you have this problem.