ISSUE 2026-09-27SERIES BREC 28TYPE BRIEFgarnetgrid.com

Everyone guards the front door. Almost nobody watches what leaves.

Insights

Most security failures in large organisations are not caused by an unknown attack. They are caused by a control that exists in configuration, has never been triggered by a real fault, and has no owner. Six places that happens, what actually goes wrong in each, and the cheap tests that tell you whether yours are real.

Inbound gets the budget. Firewalls, WAFs, segmentation: the whole perimeter diagram points one way. Outbound is usually left at default allow, because denying it breaks things. So once any process is running on a host, however it got there, it can reach anything. DNS. Port 443 to an arbitrary address. A package registry, a crash reporter, a paste site. Data rarely leaves through an exotic channel. It leaves through a permitted one.

The honesty test takes five minutes. Pick one production host and write down every destination it is permitted to reach. If the answer is "the internet, minus a blocklist", there is no egress control, whatever the data-loss console reports. A blocklist is a statement about what you happened to think of.

Going the other way is genuinely painful, and anyone describing it as a configuration change has not done it. Under a default-deny outbound policy, package installs fail, image pulls fail, updates fail, and a set of things nobody remembers fail in ways that look like unrelated application bugs: certificate revocation checks, time synchronisation, licence validation, an SDK that blocks on a timeout. The route that works is to log denied connections in report-only mode for a few weeks, build the allowlist from observed traffic rather than from memory, and accept that the list needs an owner permanently. Allowlisting by hostname through a proxy is also weaker than it looks, because it is only as good as the proxy's host and SNI checking, and names can be moved under you.

A control that has never fired is not a control

Monitoring gets written, reviewed and committed. What almost never gets tested is delivery. A check can run on schedule, correctly detect the fault, write a log line, and reach nobody: the webhook was rotated, the channel was muted during an earlier incident and never unmuted, the scheduler entry is enabled in one place and disabled in another, the rota points at a mailing list whose members have all left.

Hold on to the distinction between a log line and an alert a human acknowledged. Only the second is a control. It is easy to end up with an estate where everything is green because every check is reporting the absence of a condition it could not actually have detected.

The test is fault injection. Break the thing deliberately and measure the time until a person says they saw it. Do it on a cadence rather than once, because a control that only fires on a genuine defect stops proving anything the day you fix the defect. If you want one number for a board, this is a better one than a vulnerability count: how many of your top ten controls were proven by an induced failure last quarter, and what was the median time to a human.

Machine credentials outlive the people who created them

Human identity gets joiner-mover-leaver workflow, access reviews, MFA and attestation. Machine identity gets a token pasted into a CI variable some years ago. The blind spot is not that the credentials exist. It is that nobody has written down the blast radius of any single one of them.

Two mechanisms do most of the damage. Inheritance: a secret in an environment variable is readable by every child process of whatever holds it, so a build script, a test helper or a dependency's install hook has everything the pipeline has. And silent rotation failure: the new key is issued, the old one is never revoked, nothing breaks, and so nobody notices that the number of valid credentials only ever goes up.

A worthwhile afternoon. For the three automation credentials you use most, write the exact list of actions each can perform, then write how you would know it had been used by someone else. Most teams can do the first and not the second. Where you cannot detect misuse, scope is the only lever left and it deserves real effort. One warning: do not establish a credential's limits by attempting something destructive with it. Use a read that would fail under the wrong scope, or a resource created specifically to be broken.

Retrieval and agents inherit permissions nobody intended

The first mechanism is that retrieval flattens access control. An index built by a privileged crawler has no notion of who may read a given chunk. Unless the retriever re-checks every candidate against the source system's permissions at query time, for the identity of the person asking, each user effectively reads at the crawler's level. What comes back is a summary rather than the document, so tooling that matches on fingerprints and labels does not recognise it as the thing it was meant to protect. This stays invisible during testing, because the people testing are usually administrators with access to everything anyway.

The second is the confused deputy. An agent with tools reads content, and content can contain instructions. There is no reliable way today to separate data from instruction inside a model's context, which makes filtering for injection a losing position to defend. The control that holds is capability: what can the tool actually do, which of those actions are irreversible or reach outside the building, and is there a person in front of those specific ones. Being able to state that a system cannot send mail, cannot write to a customer record and cannot spend money is worth more than any classifier.

Running inference on hardware you own removes one class of exposure, the one where staff paste customer data into a service whose logs you will never see. It does nothing for either problem above. We would rather say that plainly than sell private infrastructure as a general answer to AI risk.

Backups are measured. Restores are not.

Backup jobs report success, and that figure travels upwards. Three things are rarely measured: what the exclude rules dropped, whether a restore works on a machine with none of your tooling on it, and whether the backups can be destroyed by the same credentials that run production.

Exclude lists are the quiet one. They get written once, usually to keep caches and build output out of the archive, and are then never revisited. Directories created later, and file types added later, fall outside the backup with no error anywhere. Anything both untracked by version control and excluded from the file-level backup exists in exactly one place, and it is usually the hand-tuned configuration that took somebody a week to get right.

Exit-status handling is the other. A job configured to treat a non-zero exit as acceptable, for whatever good reason applied at the time, will report success over a truncated dump indefinitely. The only honest verification is a restore into a clean environment, diffed against production, carried out by someone who did not build the backup.

The live system is bigger than the repository

Asset inventories describe the repository. Attackers meet what is deployed, and the two diverge in predictable ways. DNS is the most common: records still pointing at resources deallocated years ago, where whoever claims that name at the provider can serve content on your domain. Then the environments that were never meant to survive, holding a copy of production data behind weaker authentication than production has.

Third-party browser code is the one that most often sits on a page taking money. A tag manager is a deployment mechanism with its own change process, usually owned by a different department, and a script hosted on someone else's CDN can change without any release on your side. Your inventory says what is in the build. The browser's inventory is what executes. A content security policy in report-only mode for a fortnight will tell you the difference, and it normally surprises people.

The general form is worth stating. Production behaviour often lives outside the codebase, in edge rules, proxy configuration, dropped-in files and platform settings. A clean code review is entirely compatible with a live system doing something else. Security assurance based on reading the repository is measuring the wrong artefact.

None of this needs a new product, and none of it is a maturity-model exercise. Pick the three controls you would cite first if a customer sent you a security questionnaire, and this month prove each one by breaking something on purpose and measuring whether a human found out. Then put outbound connection logging into report-only mode and read it in a fortnight. Those two activities will tell you more about your real position than a year of dashboards, including which of the six things above you are currently taking on faith.

Talk to us about this