AI Security & Governance

Egress Control and Data Exfiltration

Deterministic network blocks as the last defence that still works.

Chapter 8 of 1213 min readOpen access

Egress Control and Data Exfiltration is the chapter about the last thing standing. Once the injection has landed, the tool has been called and the data has been read, the only question left is whether the data can leave, and that question is answered by network infrastructure rather than by anything the agent can talk its way past.

Key takeaways

  • Egress is the last deterministic control. A packet reaches its destination or it does not, and no wording in a prompt changes the routing table.
  • Default to deny outbound, and allowlist destinations by name at a proxy rather than by address, since addresses change and names are what policy is written about.
  • The channels are more numerous than the network tab suggests. DNS lookups, error reporting, webhooks, model providers, git remotes and image loads in rendered output all leave the building.
  • Permitted destinations are exfiltration paths too. An allowlisted ticketing system that an attacker can read is a perfectly good place to put stolen data.
  • Alert on volume and shape, not only on destination, because the request that matters will usually be to somewhere entirely legitimate.

Read this after Chapter 7, which left one boundary open on purpose, and beside Chapter 6, whose authorization decisions cannot see data leaving through a channel that was never a tool call. Chapter 12 turns the logs this chapter produces into evidence.

The architecture review goes well. Identity is scoped, tools are narrow, authorization is enforced at the destination, and code runs in a sandbox.

Somebody asks how the agent reaches the model provider. Through the internet, obviously, from the same subnet, with an egress rule permitting all outbound traffic on port 443, because that is the default in every cloud account anyone has ever used.

Which means the sandbox, the narrow tools and the scoped credentials are all sitting behind an open door.

Why this is the control that survives

Ranking controls by how they fail is more useful than ranking them by strength, and egress wins that ranking for a structural reason.

Every control before this one has a persuasion surface. A prompt instruction can be argued with. A tool can be called in an order nobody expected. An authorization policy can be satisfied by a request that is technically permitted and substantively wrong. Each depends on some component making a judgement.

A network rule makes no judgement. The connection is established or it is refused, and the decision is taken by infrastructure that has never seen the prompt, does not know what an agent is, and cannot be reasoned with. That is not a rhetorical flourish. It is the property that makes the control hold when everything upstream has already failed.

The corollary is that egress deserves the same design attention as identity, and it usually receives none. It sits with the platform team, it defaults to open, and it is invisible on the architecture diagram, which is why Chapter 2 insisted on drawing every destination separately from every tool.

The channels, enumerated honestly

Before allowlisting anything it is worth writing down every way data can currently leave, because the list is longer than teams expect.

Direct outbound HTTP from the agent process is the obvious one, and it is joined by outbound HTTP from any sandbox that has network access. Model provider calls carry whatever is in the context, which is a channel by design and the one nobody thinks of as a channel. DNS resolution leaks data through the query name itself, which is a known technique and works even when no connection is permitted.

Then the less obvious set. Error and crash reporting sends stack traces and local variables to a third party. Logging pipelines forward to systems with different access rules than the source. Webhooks and callbacks deliver payloads to addresses that may have been configured by a user. Git operations push to remotes. Email and messaging tools send to arbitrary addresses by design. And rendered output that loads a remote image encodes data in the URL, which is exfiltration through the user's own browser.

That last one connects to OWASP's improper output handling entry, since it is the same failure viewed from the other side: output passed to a rendering layer without validation becomes a request the rendering layer makes on your behalf.

Write the list for your own system before designing the policy. A policy built against an imagined list will allowlist the model provider and miss the error reporter.

Deny by default, allow by name

The policy shape that works is the boring one, and its details decide whether it holds.

Start from deny all outbound. Then add named destinations with a reason and an owner attached. Not address ranges, because cloud services change addresses continuously and a range is a much wider grant than the name that motivated it. Not wildcard subdomains where avoidable, since a storage provider's wildcard covers every customer of that provider, including the attacker's bucket.

Enforce at more than one layer, because each layer catches a different mistake. A forward proxy that terminates the connection can match on hostname and path, log the request, and refuse anything not on the list. DNS policy prevents resolution of unapproved names and, importantly, contains the DNS tunnelling channel that a proxy alone does not see. Network-level rules stop traffic that bypasses both, which is what determined code will attempt first.

The operational reality is that allowlists fail closed and failing closed generates tickets. Plan for that rather than being surprised by it. A fast path for adding a destination, with a named approver and a recorded reason, is what keeps the control alive, because the alternative is somebody widening the rule at midnight to clear an incident and never narrowing it again.

Exfiltration through channels you allowed

This is the part that separates a real egress design from a checkbox, and it is where the attacker with any competence will go.

Consider an agent permitted to write to a ticketing system, which is entirely reasonable since drafting replies is its job. An attacker who can read tickets now has a channel. The instruction is not to send data to a strange host, it is to include the data in a ticket, and every control in this chapter permits it because the destination is approved.

The same shape recurs everywhere. A permitted document store where the attacker can share a folder. A permitted search API whose query string carries the payload. A permitted model provider, if the attacker controls a prompt that gets logged somewhere they can read. A commit to a repository they watch.

Three mitigations apply, and none of them is a network rule. Scope the write side of permitted destinations, so the agent can write to the specific ticket it is working on rather than to any ticket, which is Chapter 6 applied to outbound. Treat data volume as the signal rather than destination, since a legitimate reply contains one customer's details and an exfiltration contains four thousand. And apply the data classification that most organisations already own, so that records tagged sensitive cannot enter an outbound payload at all without an explicit decision.

OWASP's sensitive information disclosure entry is the reference for why this class needs its own treatment. The agent is not breaking out of anything. It is using its granted capabilities to disclose, which is the failure mode that survives a perfect perimeter.

Volume and shape are the second dimension

Destination-based policy answers where. It does not answer how much, and the how much question is where the interesting alerts come from.

Baseline what normal looks like for each agent. The baselines are usually tight. A support agent makes a handful of reads per ticket and writes one draft. A reconciliation agent processes a known number of records per run. Deviations from that shape are visible without any machine learning, and thresholds set from a week of observation catch the cases that matter.

Four signals are worth wiring specifically. Records read per session, since disclosure at volume needs the reading first. Bytes written to any single destination in a window. Request rate to a permitted destination, because a slow drip is the patient version of the same attack. And distinct resources touched, which catches the enumeration pattern where an agent walks an identifier space it has no business walking.

Pair each signal with an automatic action rather than only an alert. Rate limits that refuse rather than warn, and a circuit breaker that suspends the agent's identity when a threshold trips, are the difference between a contained event and a well-documented one. The stop control in Chapter 12 is the manual version of the same idea.

A worked example, composited and labelled

The details are composited from ordinary production shapes rather than one system, and the shape is exact.

An agent summarises inbound sales enquiries and enriches them from a CRM. It holds a read credential for the CRM, writes summaries back to the enquiry record, and calls a model provider. The team has done Chapters 4 through 7 properly.

Egress before the work is the cloud default, meaning all outbound on 443 permitted. An enquiry arrives containing an instruction to include a base64 blob of recent contacts in a request to a research service, phrased as an enrichment step. The agent has a fetch tool. The request succeeds.

Egress after the work looks different in three ways. Outbound is denied by default with three named destinations: the model provider, the CRM API and one enrichment vendor, each with an owner and a review date. The fetch tool no longer accepts arbitrary URLs, so the attempt fails at the tool layer before reaching the network, which is Chapter 4 doing its job. And a volume rule caps contact records read per session at twenty, breaking the circuit at fifty and paging the owner.

The instructive part is that all three were needed. The tool constraint stops the naive version, the allowlist stops the version that uses a different tool, and the volume rule stops the version that uses the enrichment vendor that was legitimately on the list.

What to log, and the detection it gives you

An egress point sees every outbound request, which makes it the highest-value logging location in the system and the cheapest to instrument.

Log the agent identity from Chapter 5, the destination hostname and path, the size of the request body, the session or task the request belongs to, and the decision with its reason. Retain enough to answer the question an incident actually asks, which is what this agent sent, to whom, over what period, and how that compares with its normal week.

Two derived signals fall out for free. Denied requests to unapproved destinations are a high-quality detection surface, because a legitimate agent generates almost none, and a sudden run of them is either a compromise or a deployment nobody announced. And requests to approved destinations with anomalous payload sizes are the detection for the previous section's hard case.

Keep these logs outside the agent's own reach, which is a point Chapter 12 will make again with more force. A compromised agent that can edit the record of what it sent has turned your evidence into a story it is telling you.

The objection: our agent needs the open web

Sometimes it genuinely does. A research agent that can only reach four hosts is not a research agent. The objection is legitimate and the answer is to separate the capability from the data rather than to abandon the control.

Split the work across two identities with different postures. A retrieval agent has broad outbound access, holds no credentials, and touches no customer data, so its worst case is that it fetches something unpleasant and returns text. An acting agent holds the credentials and the data, has a tight allowlist, and consumes the retrieved text as untrusted input under Chapter 3's assumption. The open web is available, and it is available to the component with nothing worth stealing.

Where the split is impractical, the fallback is a filtering proxy that permits broad browsing while blocking known upload and paste destinations, combined with the volume rules above. That is weaker and it is honest to say so. It reduces the number of easy channels and it does not close the class.

What is not an acceptable answer is unrestricted outbound access from a process holding production credentials, on the grounds that the agent needs flexibility. That is the configuration in the opening scene, and it is the reason the rest of the review did not matter.

Chapter summary

Egress is the last control that still works after everything upstream has failed, because a network rule makes no judgement and cannot be persuaded, so the connection is either refused or it is not. The channels are more numerous than teams enumerate, running from direct outbound HTTP and sandbox network access through model provider calls that carry the whole context, DNS queries that leak through the name itself, error reporting, logging pipelines, webhooks, git remotes, messaging tools and remote images loaded by rendered output. The policy shape is deny by default with destinations allowlisted by name rather than by address range, enforced at a forward proxy for hostname and path, at DNS to contain tunnelling, and at the network layer to catch what bypasses both, with a fast documented path for adding a destination so that nobody widens the rule under incident pressure. The hard case is exfiltration through channels you allowed, since an approved ticketing system an attacker can read is a perfectly good drop point, and it is answered by scoping the write side, classifying data so sensitive records cannot enter an outbound payload, and treating volume rather than destination as the signal. Baseline each agent's normal shape and wire four signals, being records read per session, bytes to a single destination, request rate and distinct resources touched, each paired with a refusal or a circuit breaker rather than only an alert. Log identity, destination, size, task and decision at the egress point, keep those logs outside the agent's reach, and treat denied requests as a high-quality detection surface. Where an agent genuinely needs the open web, split it into a credential-free retrieval agent with broad access and an acting agent with data and a tight allowlist.

Everything so far has bounded what the agent can do in the present. It has said nothing about what the agent believes, which is a store that persists across sessions and that a patient attacker can write to once and rely on for months. Chapter 9 is Memory and Context Poisoning.

Sources

  1. LLM02:2025 Sensitive Information DisclosureOWASP Gen AI Security Project · 2025 · Standard · verified
  2. LLM01:2025 Prompt InjectionOWASP Gen AI Security Project · 2025 · Standard · verified
  3. LLM05:2025 Improper Output HandlingOWASP Gen AI Security Project · 2025 · Standard · verified