Your AI Coding Agent Is Reading Your .env Right Now You opened Claude Code in your project directory this morning. Or Cursor. Or you let Copilot index the workspace. The agent did what agents do: it read the files to build context. Your .env was one of them. Nobody typed a command to make that happen. There was no prompt, no confirmation dialog, no line in a changelog. It's simply how these tools work — and it's the reason a threat model that didn't exist three years ago now applies to almost every developer machine. The old threat model was about publishing We built good habits around secret leakage. .env goes in .gitignore. Don't paste keys in Slack. Rotate anything that touches a public repo. GitHub reported over 39 million secrets leaked across the platform in 2024, and the tooling grew up in response: push protection, secret scanning, pre-commit hooks. Every one of those defences guards the same door: the moment a secret leaves your machine because you published something. And they aren't keeping up. GitGuardian's State of Secrets Sprawl 2026 counted 28.65 million new hardcoded secrets in public GitHub commits during 2025 — a 34% jump, the largest single-year increase they've recorded. Worse, 64% of the credentials they confirmed valid back in 2022 were still valid in January 2026. Four years in public code, never rotated. An AI agent doesn't use that door. It reads the file in place, on your disk, with your permissions, and moves the contents into a context window that may live on someone else's infrastructure. From there the key can surface in a log line, a chat transcript, a shared session, or a support ticket. .gitignore has no opinion on any of that. The same report puts numbers on this: 24,008 secrets were exposed in MCP configuration files on public GitHub in 2025 — a leak surface that didn't exist two years ago. And roughly 28% of all incidents now originate in collaboration and productivity tools rather than repositories, where credentials sit exposed to automations and AI agents. Computer-use agents make it worse A coding agent reads files. A computer-use agent — Claude's computer use, OpenAI's Operator, any assistant that drives a desktop — takes continuous screenshots and ships them to a provider. Now the attack surface isn't your .env. It's everything currently visible: the key in your open editor tab, the export line still sitting in your terminal scrollback, the provider dashboard you left open in a browser tab with the key revealed. You can't .gitignore a screenshot. What actually helps: stop putting the value in the file The fix isn't a better ignore rule. It's removing the plaintext secret from the place the agent looks. The pattern is not new — Doppler (doppler run), Infisical (infisical run) and 1Password (op run) all do a version of it. Your config file holds a reference, and the real value is injected into the process environment at launch: # before — the secret is the file ELEVENLABS_API_KEY=sk_a1b2c3d4e5f6g7h8i9j0 # after — the file holds a pointer, not a secret ELEVENLABS_API_KEY=apidrop://elevenlabs-1 Enter fullscreen mode Exit fullscreen mode apidrop run -- python app.py Enter fullscreen mode Exit fullscreen mode Your code still reads os.environ["ELEVENLABS_API_KEY"]. It never learns the reference existed. But an agent reading the file finds a harmless string — one you could screenshot, commit, or stream on Twitch without consequence. This applies to anything in a .env, not just API keys. Database URLs with embedded passwords, SMTP credentials, webhook URLs, JWT signing secrets, bot tokens. Testing it against an agent Claims like this deserve evidence, so here's the setup: a fresh clone, a vault holding a recognisable test key (sk-REALSECRET-abc123xyz789), and a .env containing only a reference. The agent reads project files: cat .env # → ELEVENLABS_API_KEY=apidrop://elevenlabs-1 grep -r "sk-REALSECRET" ./project ~/.apidrop # → no match in plaintext anywhere outside the encrypted vault Enter fullscreen mode Exit fullscreen mode The agent runs the code the normal way: import os print(os.environ.get("ELEVENLABS_API_KEY")) # → None Enter fullscreen mode Exit fullscreen mode Without the resolver, the reference is never resolved. The variable is simply empty. The agent tries the resolver without the master password: apidrop run -- python3 agent_script.py # → wrong master password or corrupted file Enter fullscreen mode Exit fullscreen mode The agent has the master password: APIDROP_PASSPHRASE="testpass123" apidrop run -- python3 agent_script.py # → sk-REALSECRET-abc123xyz789 Enter fullscreen mode Exit fullscreen mode That last one is not a bug, and pretending otherwise would be dishonest. A program has to receive the key at runtime or it can't call the API. If you hand something the password, you've granted access on purpose. Which is exactly the point: the pattern separates read access from secret access. An agent can review your entire repository, explain your architecture, refactor your modules — without that access implicitly including your credentials. Those used to be the same permission. They no longer have to be. Where this doesn't apply Worth being clear about the boundary, because it's easy to oversell. This is a local developer machine technique. It works because a human is present to type a master password. On a deployed server nobody is, and setting the passphrase as an environment variable there just trades one secret for another — a worse one, since a single value now unlocks everything. For deployed workloads, use your platform's secret management: Vercel environment variables, Docker secrets, AWS Secrets Manager, whatever your host provides. It's also not a sandbox. It doesn't constrain what an agent can do once authorised. It closes the accidental path, not the deliberate one. The practical takeaway You don't need a specific tool to act on this. The question worth asking today is simply: if an agent read every file in my project right now, what would it learn that I wouldn't put in a public gist? If the answer includes a working credential, the fix is the same regardless of which tool you pick — get the value out of the file and inject it at runtime. I built API Drop because I wanted that locally: no cloud, no account, no subscription, encrypted vault on my own disk (PBKDF2-HMAC-SHA256 at 390,000 iterations, Fernet/AES), injection via a child process rather than a system hook. It's MIT-licensed and runs on Windows, macOS and Linux. But the pattern matters more than the implementation. Doppler and Infisical do this well if you want team sync and don't mind a cloud account. Use whichever fits — just stop leaving the plaintext where your tools can read it. Found a hole in the reasoning? I'd genuinely like to hear it — the threat model here is new enough that it deserves scrutiny.
Your AI Coding Agent Is Reading Your .env Right Now
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.