Most organisations address secrets by buying a store, moving values into it, and marking the item closed. The store is worth having. It is also the part of the problem that was never where the leaks happened.

A secret store changes who can fetch a value. It does not change what happens once the value has been fetched, and that is where nearly every real exposure occurs.

Remove, do not relocate

The strongest move available is not better storage. It is not having a long-lived credential at all.

Workload identity and OIDC federation replace a stored key with a short-lived token the platform issues on demand, scoped to a specific workload or a specific repository and branch. There is nothing to rotate, nothing to leak from a CI settings page, and nothing that keeps working after an engineer leaves.

A credential that expires on its own cannot be forgotten.

Every secret in a store should be examined with one question first: can this be replaced by an identity the platform already issues? For cloud API access, for database access on a managed service, for registry pulls and for service-to-service calls inside a cluster, the answer is increasingly yes. What remains is the genuinely irreducible set, which is far smaller than most estates assume.

The paths that actually leak

Where a value goes after the store hands it over
The paths a secret leaks by after a store has handed it overA secret store narrows who can fetch a value. Once fetched, the value reaches CI logs, image layers, Git history and the process environment, and from there crash dumps, observability payloads and the store’s own backups. The answers are to fetch at run time rather than build time, deliver as a file with tight permissions rather than an inherited variable, and redact in the collector before anything leaves the boundary.THE PATHS EVERYONE LISTSAND THE ONES NOBODY LISTSThe secret storenarrows who can fetcheverything below is still in front of youCI logsmasking matches the exactvalueImage layersa later delete does not removeitGit historyand every clone and forkProcess environmentinherited by childrenCrash dumps and error reportersObservability payloadsheaders in tracesBackups of the store itself
Fetched at
run time, never build time
Delivered as
a file with tight permissions, not an inherited variable
Redacted in
the collector, before egress
Better still
a short-lived identity, so there is no stored value to leak
This diagram as text
  • The secret store — narrows who can fetch — everything below is still in front of you
  • The paths everyone lists
    • CI logs — masking matches the exact value
    • Image layers — a later delete does not remove it
    • Git history — and every clone and fork
    • Process environment — inherited by children
  • And the ones nobody lists
    • Crash dumps and error reporters
    • Observability payloads — headers in traces
    • Backups of the store itself

Relationships

  • The secret store → CI logs
  • The secret store → Image layers
  • The secret store → Git history
  • The secret store → Process environment
  • Process environment → Crash dumps and error reporters
  • Process environment → Observability payloads
  • The secret store → Backups of the store itself

CI logs. A build step that echoes its configuration, a verbose flag, a failing command printing its arguments. Masking helps and is not reliable: it matches on the exact value, so a base64-encoded or partially printed secret passes straight through.

Image layers. A secret used during a build stays in the layer that used it, even if a later instruction deletes the file. Anyone who can pull the image can recover it. Multi-stage builds and build-time secret mounts exist for this.

Git history. The value is in the objects forever, and in every clone and fork. Rotation is the only action that reduces risk; history rewriting is tidying.

The process environment. Inherited by children, readable by anything that can see the process, and routinely dumped by crash handlers that print the environment for debugging.

Observability payloads. An authorisation header captured in a trace, a request body logged at debug level, a token in a URL that lands in an access log. This is why redaction belongs in the collector, before anything leaves your boundary for a hosted backend.

Backups of the store. The vault’s own backups and snapshots are a copy of every secret you have, and they are frequently protected less carefully than the store.

What good looks like

  • Fetched at run time, not build time. A secret that exists during a build is in the artefact.
  • Delivered as a file with restrictive permissions, mounted into the container, rather than as an environment variable, which removes the inheritance and process-table paths.
  • Short-lived wherever the platform supports it, so revocation is rare rather than routine.
  • Scoped per workload. A shared credential makes the blast radius of one compromise the whole estate and makes the audit question unanswerable.
  • Redacted at the collector, so what leaves the boundary is already clean.
  • Scanned for in the pipeline, blocking. Pre-commit hooks are a convenience, not a control, because they run on the machine of the person who might bypass them.
  • Rotatable without a deployment. If rotating a credential requires a release, it will not happen on the day it needs to.

Rotation is a test, not a ritual

Most estates can describe their rotation procedure and have not performed it. The first real rotation reliably finds a service that cached the value at startup and never re-reads it, a second consumer nobody knew about, and a hard-coded copy in a configuration file.

Rotate one credential deliberately, in a window, and watch what breaks. That exercise is worth more than the policy document, and it is the same shape as every other readiness exercise: the value is in having done it rather than in having described it.

What an auditor asks

Annex A 8.24 and the surrounding controls are not satisfied by owning a vault. The questions are narrower and most estates struggle with the same ones: who can read this secret, when was it last changed, is the same value used in more than one place, and can you show the access record.

An estate built on short-lived workload identity answers all four almost trivially, because there is no stored value to ask the questions about. That is the strongest argument for removal over relocation, and it is one an auditor recognises.

How STP approaches this

We start by asking which secrets can stop existing, because an estate with no long-lived credential in CI is in a materially different position from one with a well-managed vault. What remains is fetched at run time, delivered as a file, scoped per workload and redacted at the collector. Then we rotate one, deliberately, before anyone needs to, because the procedure nobody has run is the one that fails.

More on secure SDLC and application security and audit and compliance, or start a conversation.