Why Your AI Agents Are Leaking Secrets (And How to Stop It)
Agentic System Context LeaksYou scoped your agent tools. You applied least privilege. You thought your internal DB was isolated. Yet, your lower-privilege research agent just broadcast your customer’s unmasked PII to an external web search API. Welcome to the silent crisis of multi-agent context leaks.
Context is the lifeblood of any agentic system, it provides the situational awareness an agent needs to execute its tasks. As software engineering shifts toward multi-agent architectures, agents must constantly collaborate, handing off responsibilities and sharing memory. But this necessary collaboration creates a critical vulnerability. When an agent hands off a task without strict data boundaries, a context leak occurs, posing a severe risk to system security and user privacy.
What is Context Leak ?
Fig 2 Multi agent context leakA context leak occurs when one AI agent unintentionally overshares sensitive memory while handing off a task to another agent. This becomes a severe vulnerability when the downstream agent is connected to external public networks, suddenly possessing private data it never needed to complete its job.
The Vulnerability in Action In modern agentic workflows, we rely on least-privilege tool scoping to separate access levels. Consider a standard customer support architecture: (Fig 2 Multi agent context leak)
- The Triage Agent (High-Privilege): Manages user authentication. Possesses internal tools like
get_user_profile(). - The Researcher Agent (Low-Privilege): Troubleshoots technical errors. Possesses external tools like
search_web().
When a user submits a ticket, the Triage Agent pulls their profile, loading sensitive PII (credit cards, account history) into its runtime context. To troubleshoot the specific error code, it delegates the task to the Researcher Agent.
The leak happens here: Instead of passing only the error code, the Triage Agent passes its entire runtime memory. The Researcher Agent just inherited unmasked PII.
By leaking high-privilege tool outputs to an agent with outbound network tools, the system inadvertently bridges the gap between secure internal data and the public internet.
Types of Context Leaks
Security researchers classify multi-agent context leaks into four primary vectors
Downward Handoff Leak
Fig 3 Downward Handoff LeakUnsanitized sensitive data breaches privilege boundaries by leaking from a high-privilege context into a lower-privilege agent context.
- The Trigger: A user submits a ticket. The high-privilege Triage Agent queries an internal DB, returning sensitive PII.
- The Flaw: When delegating tasks to the downstream Researcher Agent, the Triage Agent sends a raw payload containing both the required error code and the unmasked PII.
Shared Memory Leak (The Black Board Problem)
Fig 4 Shared Memory Leak / The Blackboard ProblemMany multi-agent systems rely on unmonitored global shared memory (a “blackboard”) that persists across execution boundaries, causing context bloat and state bleeding.
- The Flaw: A Data Analyst Agent analyzes an account and writes a user’s Social Security Number to the global shared memory. The Summarizer Agent later reads that same global data and inadvertently includes the SSN in outbound emails.
Egress Leak (External Exfiltration):
Fig 5 Egree Leak/External ExfiltrationThis occurs when an agent uses a legitimate external tool in an unsafe way, forwarding unvalidated internal context to a public endpoint.
- The Flaw: An agent tasked with searching for a bug fix automatically embeds the user’s email address into its
search_web()query, silently logging PII into a third-party search engine.
Cross-Session Leak (State Bleed):
Fig 6 Cross Session Leak / State BleedA massive risk in distributed RAG (Retrieval-Augmented Generation) architectures where persistent memory leaks data across different user sessions due to a lack of tenant isolation.
- The Flaw: User A reports an issue, and the agent logs the conversation into a shared Vector DB. Later, User B submits a similar query. The system triggers a global semantic search, returning and exposing User A’s private session state to User B.
Possible Solves for Context Leaks
To secure multi-agent systems, we must adapt established software engineering principles, like the Principle of Least Privilege and service-oriented boundaries, to the fluid world of generative AI. Here is how to mitigate each specific type of context leak.
- Mitigating the Downward Handoff Leak: Agent DTOs Instead of passing unstructured strings between agents, use strongly typed data classes (Data Transfer Objects) to enforce strict contracts. The high-privilege agent must serialize its output to match this schema, naturally dropping any unrequested PII before the handoff.
import kotlinx.serialization.Serializable
// 1. Define a strict contract for the handoff
@Serializable
data class DebuggerHandoffDTO(
val errorCode: String,
val systemComponent: String,
val urgencyLevel: String
// Notice: No fields for userName, email, or accountData
)
fun executeSecureHandoff(rawTriageContext: String) {
// Conceptual LLM client forcing structured output matching the data class
val safePayload: DebuggerHandoffDTO = llmClient.extractStructuredData<DebuggerHandoffDTO>(rawTriageContext)
// Pass ONLY the parsed DTO to the lower-privilege agent
debuggerAgent.run(safePayload)
}2. Mitigating the Shared Memory Leak: Namespaced Memory & RBAC Move away from a global “blackboard” chat array. Replace it with a memory manager that segments state by role. Agents must explicitly request access to specific namespaces, and access is validated against a permissions map.
class SecureMemoryManager {
private val memoryStore = mutableMapOf<String, MutableMap<String, String>>()
// Define strict access controls
private val permissions = mapOf(
"summarizer_agent" to listOf("public_logs", "user_preferences"),
"data_analyst_agent" to listOf("public_logs", "secure_pii_vault")
)
fun writeState(namespace: String, key: String, value: String) {
memoryStore.getOrPut(namespace) { mutableMapOf() }[key] = value
}
fun readState(requestingRole: String, targetNamespace: String): Map<String, String> {
val allowedNamespaces = permissions[requestingRole] ?: emptyList()
if (targetNamespace !in allowedNamespaces) {
throw SecurityException(
"Security Block: $requestingRole is not authorized to read from $targetNamespace."
)
}
return memoryStore[targetNamespace] ?: emptyMap()
}
}
// Example Execution
fun main() {
val memory = SecureMemoryManager()
memory.writeState("secure_pii_vault", "user_ssn", "000-00-0000")
// Throws SecurityException, preventing the leak
memory.readState(requestingRole = "summarizer_agent", targetNamespace = "secure_pii_vault")
}3. Mitigating the Egress Leak: Tool Output Filtering Middleware Treat every outbound tool call as a potential exfiltration vector. Wrap external tool functions with middleware that sanitizes arguments using regular expressions or external NLP libraries (like Microsoft Presidio) before the payload crosses the network boundary.
fun piiScrubberMiddleware(text: String): String {
// Regex for email redaction
val emailPattern = Regex("[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+")
val sanitizedText = emailPattern.replace(text, "[EMAIL_REDACTED]")
// Add additional scrubbers for SSN, credit cards, API keys, etc.
return sanitizedText
}
fun safeSearchWeb(toolArguments: String): String {
// Agent accidentally passes: "fix error 500 for john.doe@email.com"
val safeQuery = piiScrubberMiddleware(toolArguments)
// Executes HTTP request with: "fix error 500 for [EMAIL_REDACTED]"
return executeExternalHttpRequest(safeQuery)
}4. Mitigating the Cross-Session Leak: Tenant Isolation via Metadata When using Vector Databases for historical memory, never execute a global semantic search. Enforce mandatory metadata tagging (e.g., userId, tenantId) when writing to the database, and apply hard filters on every semantic search query to isolate user contexts.
// Conceptual interface for a Vector Database client
interface VectorDB {
fun addTexts(texts: List<String>, metadata: List<Map<String, String>>)
fun similaritySearch(query: String, filter: Map<String, String>): List<String>
}
fun logSessionToMemory(userId: String, conversationText: String, vectorDb: VectorDB) {
// Ensure all embedded context is tagged with the user's unique ID.
vectorDb.addTexts(
texts = listOf(conversationText),
metadata = listOf(mapOf("user_id" to userId)) // Critical for isolation
)
}
fun searchHistoricalContext(query: String, currentUserId: String, vectorDb: VectorDB): List<String> {
// INSECURE: Returns similar issues from ANY user bypassing boundaries
// return vectorDb.similaritySearch(query, emptyMap())
// SECURE: Restricts semantic search strictly to the active user's silo
return vectorDb.similaritySearch(
query = query,
filter = mapOf("user_id" to currentUserId)
)
}Conclusion
Multi-agent systems represent a massive leap in how software operates, shifting from rigid logic to fluid, autonomous collaboration. However, this flexibility cannot come at the expense of security. As we move away from monolithic LLMs to specialized agent swarms, the traditional boundaries of software engineering, strict data contracts, least privilege access, and tenant isolation, must be woven directly into the fabric of AI communication. Context leaks might be a silent threat today, but by treating agent memory as structured, governable data rather than an open chat thread, developers can build agentic workflows that are as secure as they are intelligent.