Defense in depth against prompt injection: layers, not luck
Prompt injection keeps working because defenses keep relying on the model to say no. System-prompt instructions (“never reveal secrets”), output filters, and fine-tuned refusal behavior are each a single point of failure: the attacker only needs one injection that slips past. The realistic posture is defense in depth — several independent layers, arranged so that a successful injection at one layer still has nothing to grab at the next.
Layer 1: keep secrets out of context
The strongest layer is structural. If the agent’s context window never contains the plaintext value of a credential, then a successful injection has nothing to exfiltrate. This is context minimization applied to secrets: agents reference credentials by name, and the value is resolved elsewhere, outside the model’s working memory.
This layer is the one most setups skip, because it requires changing how tools receive secrets — but it is also the only layer that removes the payload entirely.
Layer 2: inject at the process boundary
When a command genuinely needs the secret, deliver it at spawn time, not into the prompt. A credential broker resolves the named secret and injects it into the subprocess environment as the command starts. The agent never reads the value; the tool inherits it as an environment variable.
Equally important is what happens on the way back: stdout and stderr should be sanitized for secret-shaped strings, including base64 and URL-encoded variants, so a verbose tool that echoes its environment cannot leak values into agent-visible output.
Layer 3: proxy the transport
HTTP-based tools are a second injection surface. Instead of giving the tool an API key to attach to requests, a local proxy attaches credentials per host. The tool — and the agent driving it — never handles the credential material at all. Per-host injection means the proxy only attaches the right header to the right endpoint, so even a fully compromised agent that sends a request to the wrong host gets nothing useful back. Terminating TLS locally keeps certificate verification intact.
Layer 4: DLP on the way out
Assume something leaked anyway. Route outbound LLM traffic through a DLP layer that pattern-scans requests before they leave the machine: a keyword pre-filter, then regex rules, then an entropy check to catch secrets that do not match known patterns. When a hit occurs, the layer can log it or mask the value. The configuration should reload without downtime, and a bad config should keep the previous state rather than silently disabling protection.
Layer 5: audit and detect
The layers above reduce exfiltration; the audit log tells you when they were exercised. Denied injections and redaction events are the interesting signals — they mean something tried to cross a boundary. Append-only, machine-readable events turn “did anything try to leak a secret?” into a query you can run.
Why layers beat luck
A single-control defense fails on the one injection you did not anticipate. A layered defense changes the economics: the attacker must win every layer, while the defender only needs one layer to hold. Even a model that is fully compromised still has to push the stolen value past the proxy and the DLP layer before it leaves the machine.
trustless implements this stack in one binary — process-level injection with output sanitization, a per-host credential proxy, and a DLP reverse proxy with 40 gitleaks-derived rules and entropy-based detection. The command reference is in the docs.
If your agent were fully compromised right now, which layer would stop the exfiltration — and have you tested it?