Secrets Management for AI Agents: Killing the .env File for Good
- The
.envfile was designed for a human who types a key once and rotates it occasionally. AI agents read environment variables programmatically, copy credentials at machine speed, and operate as non-human identities that vastly outnumber people. - CVE-2025-68664 ("LangGrinch", CVSS 9.3) is the proof: a serialization-injection bug in LangChain core let prompt injection extract secrets straight out of environment variables when
secrets_from_env=Truewas the default. - Under the lethal trifecta (private data + untrusted content + outbound channel), any agent with
.envaccess plus untrusted input plus a network egress path can be made to exfiltrate its secrets. - Replace static
.envstorage with five practices: centralized vault, runtime injection, per-agent scoped identity, automated rotation, and short-lived context-aware credentials via workload identity federation. - For MCP, use OAuth 2.1 with mandatory PKCE and
${env:VAR}references instead of plaintext keys inmcp.json- MCP config files were a top-growing secret-leak surface in 2025. - You cannot enforce any of this on agents you cannot see. Discovery and inventory of which agents and MCP servers still hold static, long-lived secrets is the prerequisite for the rest.
The .env file is a human convenience. One engineer pastes an API key into a text file, the app reads it on startup, and once in a while someone remembers to rotate it. That model held for a decade because there was always a person in the loop - one human, one identity, one set of hands accountable for one key.
AI agents demolish every assumption in that sentence. They are not people. They do not type keys once and forget them - they read environment variables programmatically, on every run, at machine speed. They are non-human identities that already outnumber humans by a wide margin and are multiplying faster than any IAM team can track. And, critically, they can be talked into things. A prompt-injected agent that can read a .env file is a prompt-injected agent that can leak it.
This guide makes the case that secrets management for AI agents is not a tweak to existing practice - it is a different discipline. We will walk through why the .env file must die, the verified incident that proves it, the five practices that replace it, the MCP-specific standards, and where continuous visibility fits.
The .env file was built for humans - and agents read it
A .env file or a plaintext block of environment variables is, to an autonomous agent, just another readable resource. The frameworks that orchestrate agents - LangChain, LlamaIndex, custom harnesses - pull from os.environ as a matter of course. When you give an agent the ability to execute code, browse files, or call tools, you have implicitly given it the ability to read whatever secrets sit in its process environment.
That changes the threat model entirely. A config file that a human edits in an IDE is low-risk. The same file becomes an active attack surface the moment a machine that processes untrusted input can read it on demand. The danger is not theoretical - it has a CVE.
The LangGrinch wake-up call (CVE-2025-68664)
In December 2025, researcher Yarden Porat disclosed CVE-2025-68664, codenamed *LangGrinch*, a critical (CVSS 9.3) flaw in LangChain core. A serialization-injection bug in the dumps()/dumpd() path failed to escape free-form dictionaries containing the internal lc marker, letting attacker-controlled fields - things like metadata, additional_kwargs, and response_metadata - inject LangChain object structures that were trusted during deserialization.
The payload that matters here: with secrets_from_env=True - the *previous default* - an attacker could use this to extract secrets directly from environment variables. In other words, prompt injection reaching into the process environment and pulling out API keys. The same path could enable remote code execution via Jinja2 templates. It was patched in langchain-core 1.2.5 and 0.3.81, which flipped secrets_from_env to False by default, blocked Jinja2 templates, and added an allowed_objects allowlist for deserialization.
LangGrinch is the clean proof of the thesis: when an agent framework reads secrets from the environment, prompt injection becomes secret exfiltration. The fix the maintainers shipped - stop reading secrets from env by default - is the same fix every security team should generalize.
This is the same family of credential-theft pattern we have documented across the supply chain, from the Cline GitHub Actions npm token theft to the Trapdoor cross-ecosystem credential stealer and the Hades PyPI credential stealer. The common thread: long-lived secrets sitting somewhere a process can read them.
Why agents break the human-in-the-loop assumption
Three structural shifts make agent secrets different from human secrets.
1. Non-human identities at scale
Machine identities now outnumber human ones by roughly 45:1 in the typical enterprise (Gartner's widely cited figure; cloud-native and DevOps environments run far higher). Agents are a fast-growing subset of those NHIs. GitGuardian's *State of Secrets Sprawl 2026* report - covering calendar year 2025 - counted nearly 29 million new secrets leaked in public GitHub commits, a 34% year-over-year jump and the largest single-year increase on record. Secrets tied to AI services specifically grew 81%, and 8 of the top-10 fastest-growing leaked secret types were tied to the AI ecosystem. The human-paced controls we built for a handful of named users do not scale to thousands of autonomous identities.
2. Machine-speed sprawl
Agents accumulate and copy credentials as fast as they execute. A human leaks a key occasionally and by accident. An agent can read, log, embed, and forward a key thousands of times in a session - and notably, GitGuardian found that commits authored with AI coding tools leaked secrets at roughly twice the human baseline rate.
3. The lethal trifecta
Simon Willison coined the lethal trifecta in June 2025: an agent that has (1) access to private data, (2) exposure to untrusted content, and (3) the ability to communicate externally is a near-guaranteed exfiltration risk under prompt injection. A .env-reading agent that also processes untrusted input and has any outbound channel checks all three boxes. We cover the dynamic in depth in the lethal trifecta and agent data exfiltration and indirect prompt injection explained.
The practical reframe: treat exposure to untrusted content as a taint event, and once an agent is tainted, block exfiltration-capable actions. But the cleaner structural fix is to remove the private-data leg of the trifecta entirely - don't let the agent hold the secret in the first place.
The five practices that replace the .env file
Each practice kills a specific anti-pattern. None of them is novel infrastructure - they are established primitives, applied with agent-scale discipline.
| Practice | Anti-pattern it kills | Primitive |
|---|---|---|
| Centralized vault | Keys hardcoded in source, prompts, or .env | HashiCorp Vault, AWS/GCP/Azure secret managers |
| Runtime injection | Secrets written to disk and persisted | In-memory injection at execution, cleared after use |
| Per-agent scoped identity | Shared static keys, no attribution | SPIFFE/SVID, scoped NHI per agent |
| Automated rotation | Quarterly or never human rotation | Machine-speed, hands-off rotation |
| Short-lived credentials | Long-lived keys waiting to be stolen | Dynamic secrets, JWT-SVID, workload identity federation |
Practice 1 - Centralize in a vault
Never hardcode keys in prompts, source, or .env. Use a dedicated secrets manager as the single source of truth, and pick one that supports machine-to-machine authentication and automated rotation. The agent should not *store* credentials at all - it retrieves them from the vault at runtime and is granted only what its identity is authorized for.
Practice 2 - Inject at runtime, never persist
Deliver secrets into agent memory only at execution time, hold them only as long as needed, clear them after use, and never write them to disk. This is the direct contrast to the static .env/JSON pattern. For MCP specifically, use ${env:VAR} reference syntax in mcp.json rather than plaintext, and keep the actual values in a manager outside source control.
Practice 3 - Give every agent its own scoped identity
Each agent gets a unique non-human identity, least-privilege scope, an assigned human owner, and inclusion in IAM recertification. This maps to OWASP's Excessive Agency risk - the compound of excessive functionality, excessive permissions, and excessive autonomy. SPIFFE IDs are structurally correct here: they are tied to workloads, not people, and SPIRE auto-rotates the short-lived SVIDs. The result is distinct, auditable, accountable agents instead of a fog of shared keys. See least privilege for AI agents and non-human identity governance for the identity model in full.
Practice 4 - Rotate automatically, at machine speed
Rotation cannot depend on a human remembering. Industry surveys consistently find that a large majority of organizations fail to rotate secrets within recommended intervals, and that long-lived credentials with no expiry configured account for a substantial share of secret-related policy violations. Agents need rotation that happens without intervention - ideally so frequent that rotation stops being an event and becomes the default state. Automated rotation enforces least privilege over time, shrinks the dwell time of any exposed secret, and supports continuous compliance.
Practice 5 - Prefer short-lived, context-aware credentials
The endgame is having no long-lived secret to steal. Use dynamic secrets generated on demand with built-in expiry (a temporary AWS session token valid for minutes, for example). Provision an ephemeral identity when the agent spins up and retire it when the task completes. And use workload identity federation - AWS IAM Roles Anywhere, GCP Workload Identity Federation, Azure federated credentials/managed identity - so the agent exchanges an OIDC JWT or JWT-SVID for a short-lived cloud token with no stored secret at all. SPIRE can act as the OIDC IdP the cloud trusts. Just-in-time access means the agent gets exactly what it needs, exactly when it needs it, and nothing lingers.
MCP, OAuth 2.1, and the standards to cite
MCP servers are their own fast-growing leak surface. GitGuardian found MCP-related configuration files leaked over 24,000 unique secrets on public GitHub in 2025, with Google API keys (~20%) and PostgreSQL connection strings (~14%) leading. A Trend Micro review of more than 19,000 MCP server codebases found that roughly 48% recommend storing secrets in insecure .env files or plaintext JSON, and Trend Micro separately documented hardcoded cloud credentials shipped in MCP server configs as a common, dangerous pattern that hands attackers a direct line to the cloud provider's API.
The authorization story has matured fast. The March 2025 MCP spec standardized OAuth 2.1. The June 2025 revision split MCP servers from authorization servers and required Protected Resource Metadata (RFC 9728) plus resource indicators (RFC 8707) so tokens are bound to the specific server. The November 2025 revision made PKCE mandatory for every client (S256 only, with plain PKCE banned) and added Client ID Metadata Documents. The short version: OAuth 2.1 with mandatory PKCE and resource-bound tokens is the standard to adopt - not static keys. We go deeper in OAuth for MCP servers explained and the MCP server security guide.
How this maps to OWASP
In the 2025 OWASP Top 10 for LLM Applications, Sensitive Information Disclosure climbed from #6 to #2 - the recommended mitigations are to never put secrets in prompts, apply RBAC before data reaches the model, and redact. Excessive Agency (LLM06) is the companion risk, mitigated by least privilege on tools and APIs and human approval for sensitive actions. OWASP's GenAI Security Project also published a dedicated Top 10 for Agentic Applications on December 10, 2025, with identity and privilege abuse among the headline threats. The throughline: as models gain the power to call APIs and execute code, the blast radius of a single prompt injection or over-scoped identity becomes catastrophic. The OWASP Top 10 for LLM applications guide and OWASP Top 10 for agentic applications guide lay out the full control set.
What a security team actually does
- Inventory every agent and MCP server, and identify which ones read secrets from
.envor environment variables today. - Move all secret values into a centralized vault and replace plaintext config with runtime injection and
${env:VAR}references. - Issue each agent a distinct, least-privilege non-human identity with a named owner; enroll those identities in IAM recertification.
- Turn on automated rotation and migrate the highest-value keys to dynamic, short-lived credentials via workload identity federation.
- Adopt OAuth 2.1 + PKCE (S256) with resource-bound tokens for all MCP connections.
- Treat untrusted-content exposure as a taint event and gate exfiltration-capable actions behind it; monitor for anomalies.
- Re-scan continuously - agents and MCP servers appear faster than tickets close.
You can't fix what you can't find
Every practice above is useless on an agent you don't know exists. The five controls assume you already have a complete, current list of which agents and MCP servers run where, what identities they hold, and which ones are still reading static secrets from a .env. In most organizations, that list does not exist - which is the whole reason AI agents are the new shadow IT.
This is where continuous agent and MCP visibility - the category Anomity operates in - fits. The job is discovery and inventory: surface which agents and MCP servers still hold static, long-lived, or over-broad secrets, flag over-scoped identities, and produce the audit trail that proves remediation. It is complementary to vaults and federation, not a replacement - it is the layer that finds the .env-era holdouts so the rest of the program can act on them. See how to build an AI agent inventory and inside Anomity discovery for how that works in practice.
Kill the .env file. But first, find every place it still lives - because you can't govern what you can't see.
Frequently asked questions
Why is the .env file dangerous for AI agents specifically?
Because agents and the frameworks running them read environment variables programmatically. A .env file is no longer a passive config a human edits - it is a machine-readable target that an agent can be tricked into reading and exfiltrating. CVE-2025-68664 (LangGrinch) demonstrated exactly this: prompt injection extracting secrets out of environment variables via LangChain's serialization path when secrets_from_env=True was the default. Combine that with the lethal trifecta and a single injected prompt can leak everything in the file.
What is the best way to manage secrets for AI agents?
Stop storing secrets where the agent can statically read them. Centralize them in a vault (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault), inject them into agent memory only at execution time, give each agent its own scoped non-human identity, rotate automatically at machine speed, and prefer short-lived dynamic credentials obtained through workload identity federation so there is no long-lived secret to steal.
What is workload identity federation and why does it matter for agents?
Workload identity federation lets an agent exchange an OIDC token (or a JWT-SVID from SPIFFE/SPIRE) for a short-lived cloud credential - via AWS IAM Roles Anywhere, GCP Workload Identity Federation, or Azure federated credentials - without ever storing a long-lived secret. The agent proves what it is, receives a token valid for minutes, and the blast radius of any leak collapses to that window.
How should secrets be handled in MCP servers?
Never put plaintext keys in mcp.json or a .env shipped with the server. Use reference syntax such as ${env:VAR} that resolves from a secrets manager, and adopt the MCP authorization standard: OAuth 2.1 with mandatory PKCE (S256, required for every client as of the November 2025 spec revision), Protected Resource Metadata (RFC 9728), and resource indicators (RFC 8707) that bind a token to the specific server it was issued for.
How often should AI agent credentials be rotated?
Far more often than human credentials. Humans rotate quarterly or never; industry surveys consistently find a large majority of organizations miss recommended rotation intervals. Agents should use automated, machine-speed rotation - ideally short-lived dynamic secrets that auto-expire in minutes to hours so rotation is continuous rather than a scheduled event. Long-lived static keys are a leading source of secret-related violations.
What is the lethal trifecta and how does it relate to secrets?
Coined by Simon Willison in June 2025, the lethal trifecta is the combination of three agent capabilities: access to private data, exposure to untrusted content, and the ability to communicate externally. When all three are present, prompt injection can turn the agent into an exfiltration tool. An agent with .env access (private data), untrusted input, and a network channel can be instructed to read and send its own secrets - which is why removing static secret access is the structural fix.
Should AI agents share an identity or have their own?
Each agent should have its own distinct non-human identity with least-privilege scope, an assigned owner, and inclusion in IAM recertification. Shared static keys make it impossible to attribute actions, revoke access surgically, or scope permissions. Per-agent identity - ideally a SPIFFE ID with short-lived auto-rotated SVIDs - makes each agent distinct, auditable, and accountable, and maps directly to OWASP's Excessive Agency control.
How do I find which agents still have static secrets?
You discover and inventory them. Continuous agent and MCP visibility platforms (the category Anomity operates in) scan configs, environments, and running agents to surface which ones still hold static, long-lived, or over-broad credentials, flag over-scoped identities, and produce an audit trail. You cannot migrate to vaults and federation what you have not first found.




