Skip to content

Guide10 min read

Your internal assistant answers anyone with anything in the index. Fix it before retrieval.

Internal RAG assistants leak because the index is built with an administrator's access and queried by everyone. The case for putting the permission check inside the retrieval query, the two strongest objections, and why caches, summaries and revocation belong to the same argument.

IntertraceSecurity engineering

Field guides from Intertrace security engineering for the people who run AI programs.

October 10, 2026 · 10 min read

RAG SecurityAccess ControlData LeakageOWASP
Contents
  1. 01The index runs with the indexer's access
  2. 02The claim: if the filter is not in the query, something downstream has already read the text
  3. 03Objection one: filtering after retrieval is simpler and nearly as good
  4. 04Objection two: a metadata filter is only a string
  5. 05The same argument applies to every copy of the answer
  6. 06Revocation is where the claim gets measured
  7. 07What the claim does not cover: who can write to the index

Six weeks after Kestrel Financial launched its internal assistant, someone in finance asked it about this year's bonus pools and got a precise answer, drawn from a compensation planning spreadsheet that six people in HR were supposed to be able to open. The citation link, when clicked, returned access denied. The assistant had read the file anyway. Kestrel is a fictional lender, and its ingestion job ran as a service account a platform engineer had granted read access to every drive, so indexing would not fail on permission errors, and it stored each chunk with a source URL and a title but not who could open the file.

A cited source the reader cannot open is the signature of this failure. The source system enforced its permissions perfectly; the assistant was not asking it. The argument of this piece is that the permission check belongs in exactly one place, inside the retrieval query, built on the server from the caller's verified identity, and that every other placement leaks something. There are two serious objections. One loses outright; the other changes how the check has to be built, not where it goes.

The index runs with the indexer's access

An indexer has to read documents to index them, so it runs under an account with broad read access to the wiki, the shared drives and the ticketing system. Each person who queries the assistant has a small, specific subset of that access. If per-document permissions do not travel from the source into the index, every query runs with the indexer's access, not the reader's. Kestrel's pipeline retrieved the twenty most similar chunks from the whole index, reranked them and passed the top eight to the model. Every component behaved as designed, and no step asked whether the person could read the source.

OWASP's LLM08:2025, Vector and Embedding Weaknesses, names the risk directly: in a vector database shared by several groups, embeddings from one group can be retrieved for another group's queries, leaking sensitive business information. It recommends fine-grained access controls, permission-aware vector stores, and strict logical and access partitioning of datasets [1]. None of that happens by default when a pipeline chunks text, embeds it and writes vectors.

The claim: if the filter is not in the query, something downstream has already read the text

Teams that discover the problem usually add a check, and where they add it matters more than how good it is. There are three common placements, and only one keeps restricted text away from every downstream component.

Three places to put the permission checkFig. 1
AFTER THE ANSWERToo latethe model already read itWhole indexTop-k searchall documentsModel readsevery candidateOutput filterafter disclosureAFTER RETRIEVALLeakyrerankers and logs saw itWhole indexTop-k searchall documentsApp filterdrops resultsModel readswhat is leftINSIDE RETRIEVALCorrectnever leaves the storeWhole indexFiltered searchACL from verified identityModel readspermitted chunks onlyput the permission predicate in the query itself, built from who is asking, so nothing downstream sees restricted text

Filtering the answer is too late because the model has already read the text. Filtering in the application after retrieval still exposes candidates to rerankers, logs and caches. Filtering inside the retrieval query means unauthorized chunks never leave the store.

Output filtering asks the model, or a classifier after it, to withhold what it should not have seen. That is a disclosure control applied after the disclosure, and a summary or paraphrase can carry the restricted fact without matching any pattern. It fails on its own terms.

Objection one: filtering after retrieval is simpler and nearly as good

This is the serious objection. Retrieve globally, then drop the chunks the caller cannot read before they reach the model. It needs no change to the vector store and keeps the permission logic in application code where the team already works.

It loses on two counts. By the time the filter runs, the candidates have passed through the reranker, any debug logging and any trace exporter, which are copies of restricted text in places with their own readers. And when the filter removes most of the top results, the answer degrades, which tempts developers to raise the retrieval limit or switch the filter off. A design whose failure mode invites people to disable it is not nearly as good. Put the predicate in the query itself, so unauthorized chunks never leave the store.

Objection two: a metadata filter is only a string

This one is true, and it changes how the claim has to be built rather than whether it holds. Microsoft's documentation for Azure AI Search describes the security filter pattern: store group or user identifiers in a filterable field on each document, then filter every query by the caller's groups [2]. It is candid about the limits. The principal in that field is a string used in a filter expression, with no authentication or authorization behind it; making the field non-retrievable only keeps it out of results and is not field-level security; and authorization is enforced only by applying the filter to every query [2]. The same is true of every vector store with metadata filtering. The filter is exactly as trustworthy as the identity your application puts into it, so the identity is where the rest of the work goes.

Each chunk needs enough metadata to decide access without going back to the source: parent document ID, source system, tenant, allowed principals (users and groups), the time permissions were last synchronized, and a classification label if you use one. The chunk stays linked to its parent, so a permission change or deletion reaches every chunk derived from it.

  • Store group IDs, not expanded user lists, and resolve the caller's groups at query time from your identity provider. Expanding at ingestion freezes membership: new joiners cannot find documents and people who left a team still can.
  • Resolve inherited permissions. Effective readers often come from a folder, space or project, and a connector that reads only the file's own sharing entries misses them.
  • Decide explicitly how to treat link and organization-wide sharing. Anyone with the link is not everyone in the company, and indexing it as public turns an obscure link into a search result.
  • Fail closed on missing metadata. A chunk with no permission data is readable by no one until the connector fills it in.

The filter's inputs, the user, their tenant and their current groups, come from the authenticated session, resolved on the server, never from the request body, a query parameter or the model. A retrieval tool an agent calls should not accept a user or groups argument at all. A model that can choose whose permissions to search with will eventually be asked to. The OWASP Authorization Cheat Sheet's rule applies unchanged: never rely on client-side access control checks as the decisive factor [3].

Check the side doors. Many assistants have a second tool that fetches a full document by ID after a search returns a snippet. If it does not repeat the check, anyone who can guess or harvest an ID can read the document, the insecure direct object reference problem the cheat sheet addresses by requiring access control checks on every request for the specific object [3]. If the same index also serves an API or a plugin, a filter applied only in the chat interface protects only the chat interface. When you serve more than one customer, partition by tenant at the storage level so a bug in the filter cannot cross a tenant boundary [1].

What must carry the permission at each stage, and how it usually fails
StageWhat must carry the permissionTypical failureTest that catches it
IngestionEach chunk's parent ID, allowed principals and sync timeConnector drops sharing data or ignores inherited permissionsIndex a document shared with one test group; inspect its chunks' metadata
RetrievalA filter built from the caller's verified groups, inside the queryGlobal top-k search, filtered afterwards or not at allAsk a question only a restricted document answers, as an unauthorized user
Fetch by IDThe same check, repeated for the specific documentFull-document tool trusts the ID from the search resultCall the fetch tool directly with a restricted document's ID
Reranking and logsOnly already-filtered candidatesDebug logging or traces capture every candidateSearch the trace store for a canary phrase after a denied query
Answer cacheKey includes the caller's permission setSemantic cache serves one user's answer to anotherAsk the same question as two users with different access
Summaries and memoryThe intersection of every source's readersPrecomputed digest readable by everyone in the workspaceRevoke access to one source of a digest and query the digest

The same argument applies to every copy of the answer

Anything that stores retrieved text or a generated answer is a copy of the restricted data and needs its own access rule. Semantic caches are the most common leak: they return a stored answer for any question that is similar enough, so unless the key includes the caller's permission set, the first person to ask decides what everyone else sees. Key the cache by a hash of the caller's groups and the sources' permission revision, or cache retrieved document IDs and re-filter on every hit. Retrieved passages logged in full to an observability platform every engineer can search are a copy too.

Summaries and digests combine sources, so their readers are the intersection of every source's readers, not the union and not the readers of the space where the digest is stored. Conversation memory is a copy: a person who loses access to a project should not keep getting answers from a summary the assistant wrote for them last month. Titles and counts leak as well. An assistant that confirms a restricted document exists while withholding its contents has still disclosed something. Decide how long each copy lives and what invalidates it, next to the source systems' own retention rules.

Revocation is where the claim gets measured

When access is removed at the source, the assistant keeps answering until every copy catches up. The exposure window is the slowest copy, not the fastest. Measure it, publish it internally, and set a target for sensitive collections.

How long a revoked person can still get answersFig. 2
Access revokedin the source systemT0Index ACL updatedconnector catches upSYNC LAGCaches expireanswers, passagesCACHE TTLSummaries rebuiltdigests, briefingsREFRESHMemory clearedchat history, notesRETENTIONEXPOSURE WINDOW = THE SLOWEST COPYuntil every copy catches up, the revoked person can still get answers from the document

Access is removed at the source first; the index permissions, caches, summaries and memory each catch up on their own schedule. The exposure window ends only when the slowest copy does, so measure each one.

Deletion has the same shape plus one fact: vectors are not anonymous. LLM08 notes that attackers can invert embeddings and recover significant amounts of the source information [1]. Deleting a document's text while its embeddings stay in the index is not deletion. Remove the chunks, vectors, cached answers and passages, and summaries derived from the document, and keep whatever audit evidence you need under a separate, access-controlled policy.

The most reliable test is a canary document: a file containing a unique, harmless phrase, shared with one test account. Confirm the account can retrieve it, revoke access at the source, and record how long until search, citations, cached answers and follow-up questions all stop returning the phrase. Repeat for removal from a group and for deletion of the file, then check the vector store for the canary's embeddings. If the connector's sync fails, quarantine stale chunks from sensitive collections until it recovers rather than serving them on old permissions.

What the claim does not cover: who can write to the index

Access control decides who may read a document. It says nothing about whether the document is true or carries instructions aimed at the model. PoisonedRAG, presented at USENIX Security 2025, reports a 90% attack success rate when five malicious texts are injected per target question into a knowledge base of millions of texts, and found the defenses it evaluated insufficient [4]. That describes the researchers' setup, not the compromise rate of any particular assistant, but it shows why write access to the corpus matters as much as read access. List who can write into each indexed source, including external parties who file tickets or comment on shared pages. LLM08 recommends accepting data only from trusted, verified sources and auditing the knowledge base for hidden content and poisoning [1].

Treat retrieved text as untrusted input, and keep any consequential action the assistant can take under a policy that does not depend on what a retrieved document says. Intertrace's gateway sits inline between an assistant and the models, tools and MCP servers it calls, so retrieval tool calls and model responses pass one point of enforcement: tenancy from the runtime key rather than the request body, PII and secrets detection and redaction on requests and responses, DLP over tool-call arguments, and a structured event per decision for your SIEM. It does not know your document permissions. Permission-aware retrieval, built from your source ACLs and identity provider, is a change only your retrieval service can make.

Sources

Reporting, research and specifications this piece draws on. Links go to third-party sources; analysis and recommendations are Intertrace’s own.

  1. 01LLM08:2025 Vector and Embedding Weaknesses · OWASP Gen AI Security Project
  2. 02Security filter pattern (document-level security trimming) · Microsoft Learn, Azure AI Search
  3. 03Authorization Cheat Sheet · OWASP Cheat Sheet Series
  4. 04PoisonedRAG: Knowledge Corruption Attacks to Retrieval-Augmented Generation of Large Language Models · Zou, Geng, Wang and Jia, USENIX Security 2025

About this desk

Intertrace

Security engineering

Field guides from Intertrace security engineering for the people who run AI programs.

All 22 pieces by Intertrace

Dated October 10, 2026 · added to intertrace.ai October 17, 2026

+ Runtime enforcement

See these controls on your own agent traffic.

Intertrace sits inline between your agents and the models, tools and MCP servers they call, and decides every call before it runs.

Keep reading

All insights →