TL;DR: Your .env file stores secrets in plain text. It leaks through logs, crash dumps, Docker layers, ps output, child processes, and — since 2025 — AI coding agents that read your entire project directory. The fix isn't better .gitignore rules. It's keeping secrets out of files entirely.
The short answer: no
Environment variables were never designed for secrets. They were designed for configuration — things like LOG_LEVEL=debug or NODE_ENV=production. Somewhere along the way, the twelve-factor app methodology told us to put everything in env vars, and we started storing database passwords next to log levels.
In 2026, the attack surface around .env files has expanded dramatically. Here's what's changed.
7 ways your .env file leaks secrets
1. AI coding agents read your filesystem
This is the newest and most underestimated risk. AI coding agents load your entire project directory into their context window — including .env files.
Your API keys, database passwords, and JWT secrets are sent to LLM providers as part of the conversation. .gitignore doesn't help: these tools read the filesystem directly, not the git index.
Research from Knostic confirmed that Claude Code loads .env files without explicit permission. Cursor and GitHub Copilot use the same filesystem-based approach — they index your project directory to provide context. A Wiz study found AI-assisted code regularly leaks secrets in public repositories.
2. Logs and crash dumps capture them silently
Environment variables appear in places you don't expect:
- Error stack traces in production
- Crash dump files
- Debug logs when frameworks log the full request context
- APM tools that capture process metadata
The Seneca framework vulnerability (CVE-2019-5483) exposed environment variables in error messages, revealing cloud API keys in publicly accessible crash reports. This isn't an isolated case — it's a pattern.
3. Process listings expose them
On any Unix system, environment variables are visible through ps and /proc:
# Anyone on the system can see your secrets
cat /proc/<pid>/environ
# Or via a path traversal vulnerability
curl http://your-app.com/public/../../../../proc/self/environ
This has been exploited in real attacks. If an attacker gains read access to the filesystem — even through a minor vulnerability — your secrets are exposed.
4. Docker images embed them permanently
Approximately 10% of images on Docker Hub leak sensitive data. Common mistakes:
# WRONG — secrets visible in image history
ENV DATABASE_URL=postgres://user:pass@host/db
COPY .env /app/.env
# Anyone can extract them
docker history your-image --no-trunc
Build arguments (ARG) aren't better — they persist in image layers. Even if you delete the .env file in a later layer, it exists in the build cache.
5. Child processes inherit everything
When you spawn a subprocess, it inherits all parent environment variables by default:
// Every secret is passed to this child process
const child = exec('some-command')
// The child process inherits all of process.env:
// DATABASE_URL, STRIPE_KEY, JWT_SECRET...
This violates the principle of least privilege. A logging script doesn't need your Stripe API key, but it gets it anyway.
6. No access control or audit trail
Environment variables have no concept of:
- Who accessed a secret
- When it was last rotated
- Which processes actually need it
- Whether it's been compromised
There's no way to revoke a single variable without restarting the entire process. There's no history, no versioning, no alerts.
7. They don't rotate
Static secrets are the most dangerous kind. The GitGuardian 2025 State of Secrets Sprawl report found that 70% of secrets leaked as far back as 2022 are still active today. If a key in your .env file was compromised two years ago, it probably still works.
The hybrid approach isn't enough
Doppler's take on this recommends a "hybrid approach": keep non-sensitive config in env vars, move secrets to a manager. That's solid advice. Tools like Doppler, Infisical, and Keyway all support runtime injection (doppler run, infisical run, keyway run) that injects secrets into process memory without writing files.
But many teams still export secrets to .env files for local development:
# Common pattern — downloads secrets to a file for local use
doppler secrets download --no-file --format env > .env.local
infisical export > .env.local
The moment secrets land in a file, you're back to square one. Your AI coding agent can read them. Your Docker build can embed them. The file can be committed accidentally.
The zero-disk approach
The real fix is to never write secrets to disk at all. Inject them directly into process memory at runtime:
# Keyway — secrets exist only in process memory
keyway run -- npm start
# 3 secrets injected. No file on disk. AI agents see nothing.
# When the process stops, secrets vanish.
This eliminates every attack vector we've discussed:
| Attack vector | .env file | Secrets manager (file export) | Zero-disk injection |
|---|---|---|---|
| AI agent reads filesystem | Exposed | Exposed | Safe |
| Logs / crash dumps | Risk | Risk | Risk* |
Process listing (/proc) | Exposed | Exposed | Exposed** |
| Docker image layers | Risk | Risk | Safe |
| Accidental git commit | Risk | Risk | Safe |
| Child process inheritance | Exposed | Exposed | Exposed** |
| No rotation / audit | No audit | Audit available | Audit available |
* Log redaction (e.g., Pino's redact option) mitigates this.
** Process memory is still accessible to root, but there's no file to accidentally share, commit, or embed.
Zero-disk doesn't solve everything, but it eliminates the most common leak vectors: files on disk that get committed, copied, embedded, or read by AI tools.
When .env files are still fine
Not everything needs a secrets manager. The rule is simple:
If the value leaked, could it grant access or cause damage?
- Yes → secrets manager:
DATABASE_URL,STRIPE_SECRET_KEY,JWT_SECRET,AWS_ACCESS_KEY_ID - No → env var is fine:
LOG_LEVEL,NODE_ENV,NEXT_PUBLIC_APP_URL,PORT
For non-sensitive configuration, .env files are perfectly adequate. The problem is mixing configuration and secrets in the same file.
Migration: from .env to zero-disk in 10 minutes
Step 1: Audit your .env file
Separate secrets from configuration:
# .env.local (BEFORE)
DATABASE_URL=postgres://user:pass@host/db # ← secret
STRIPE_SECRET_KEY=sk_live_... # ← secret
JWT_SECRET=super-secret-key # ← secret
LOG_LEVEL=debug # ← config (safe)
NEXT_PUBLIC_APP_URL=http://localhost:3000 # ← config (safe)
PORT=3000 # ← config (safe)
Step 2: Move secrets to a manager
# Push secrets to Keyway
keyway init
keyway set DATABASE_URL "postgres://user:pass@host/db"
keyway set STRIPE_SECRET_KEY "sk_live_..."
keyway set JWT_SECRET "super-secret-key"
Step 3: Keep only config in .env
# .env.local (AFTER — no secrets)
LOG_LEVEL=debug
NEXT_PUBLIC_APP_URL=http://localhost:3000
PORT=3000
Step 4: Run with injection
# Secrets from Keyway + config from .env
keyway run -- npm start
Your .env file now contains zero secrets. AI agents, Docker builds, and accidental commits can't leak what isn't there.
Validate at startup
Whether you use .env files or a secrets manager, always validate that required variables exist before your app starts:
import { z } from 'zod'
const env = z.object({
DATABASE_URL: z.string().url(),
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
}).parse(process.env)
This catches missing variables at startup instead of at 3 AM when a customer hits the one code path that needs the missing key.
Security checklist
Before you ship:
- No secrets in
.envfiles — only non-sensitive configuration -
.envand.env*.localin.gitignore - Pre-commit hook scanning for leaked secrets
- Secrets injected at runtime via secrets manager
- Startup validation with Zod or equivalent
- Log redaction for sensitive headers and tokens
- Secret rotation process documented and automated
- AI coding tools can't access secrets (zero-disk approach)
Further Reading
- Claude Code and Cursor Send Your API Keys to the Cloud — deep dive on the AI agent risk
- Best dotenv Alternatives for 2026 — compare your options
- Environment Variables Best Practices — naming, validation, rotation
- Keyway Security Architecture — how we encrypt secrets with AES-256-GCM
Your .env file was a reasonable solution in 2015. In 2026, with AI agents reading your filesystem and Docker images leaking secrets at scale, it's a liability. The fix isn't complicated — it's just different from what we're used to.