AI 코딩 에이전트가 지금 .env를 읽고 있습니다

작성자

카테고리:

← 피드로
DEV Community · OLI · 2026-07-21 개발(SW)

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.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다