kosa8 / Guides

Guide · Secrets and AI coding agents

How do I stop AI coding agents from reading my .env secrets?

Updated

Take the secrets out of the agent's reach instead of asking it to look away. Ignore files and deny rules cover only some of the ways an agent can read a file: Claude Code ignores .claudeignore entirely, its Read deny rules do not cover scripts that open files themselves, and Cursor says .cursorignore cannot stop its terminal and MCP tools. Keep real keys outside the folder the agent works in, and give the agent a placeholder that a proxy outside its sandbox swaps for the real key.

01Steps

  1. Find the secrets in the project. List every .env file the agent could open, including ones git ignores.
  2. Move them out and keep a template. Store the real values outside the project, load them into your own shell when you need them, and commit a .env.example with dummy values so the agent still knows which variables exist.
  3. Add deny rules as a second layer. In Claude Code, Read deny rules block its file tools and the file commands it recognises in Bash, such as cat and head. They hold in every permission mode.
    .claude/settings.json
    {
      "permissions": {
        "deny": ["Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)"]
      }
    }
  4. Install kosa8 and check which kits broker their keys. The KEYS column says where each kit's credentials go: brokered means the key stays on your computer and the sandbox holds a placeholder; passed in means the real key is in the sandbox's environment.
  5. Run the agent with its key injected at the boundary. The dry run shows the brokering line before anything starts; then run the agent on the project folder only. ANTHROPIC_API_KEY stays in your shell and the kosa8 service.
  6. Broker your own API's key the same way. Any HTTP API whose client accepts a base URL can be brokered. Describe the credential in a kit file: the upstream, the host variable holding the key, the header, and the methods and paths it may be used on.
    weather.yaml
    name: weather
    image: alpine:latest
    command: [sh]
    egress_allow:
      - docs.example.com
    credentials:
      - name: weather
        upstream: https://api.example.com
        secret: WEATHER_API_KEY
        header: Authorization
        scheme: "Bearer "
        methods: [GET]
        paths: [/v1/forecast]
        env:
          WEATHER_API_URL: "{{url}}"
          WEATHER_API_KEY: "{{placeholder}}"
  7. Check what left the sandbox. Every brokered request, allowed or refused, is logged with host, method, path and status, never the key.

02Does .claudeignore stop Claude Code reading .env?

No. Claude Code's permissions documentation says a .claudeignore file has no effect and that its entries should be moved into Read deny rules such as Read(./.env). Those rules cover Claude's file tools and the file commands it recognises in Bash, such as cat and head, but not a command like grep -r run from the folder that holds the file, or a Python or Node script that opens files itself.

Claude Code's built-in /sandbox can block reads at the operating-system level with sandbox.filesystem.denyRead, but it wraps shell commands only, and its default still allows reading credential files unless you deny them.

03Does .cursorignore keep .env away from Cursor's agent?

Partly. .env files are on Cursor's default ignore list, but Cursor's documentation says the terminal and MCP server tools used by Agent cannot block access to files covered by .cursorignore, and that complete protection is not guaranteed due to LLM unpredictability.

04How can a prompt injection steal environment variables?

Anything in the agent's environment or working folder can be read by the agent, by the packages it installs and by the scripts it runs. Text hidden in an issue, a web page or a README can tell the agent to print a key or send it to a server, and a compromised dependency can read it at install time. Prompt injection is first on the OWASP Top 10 for LLM applications.

Once copied out, a real key works from anywhere until you revoke it. That is why the reliable fix is to leave nothing worth stealing inside the agent's machine.

05What does injecting secrets at runtime through a proxy change?

The agent never holds the key. In kosa8 the sandbox gets a random placeholder and the address of an egress broker on your computer; the broker checks the method, path and placeholder, writes the real key into the header, and forwards the request only to the one upstream that key belongs to. Copied out of the sandbox, the placeholder is a random string.

  • The folder is still visible. A .env inside the folder you share is readable in the VM, so step 2 still matters.
  • Only HTTP APIs. The broker works for clients that accept a different base URL. A database password or an SSH key cannot be brokered this way; give the agent a scoped, throwaway one if it needs one.
  • The placeholder is a capability. Inside its own sandbox, while it runs, it can make any request the route allows. Narrow the methods and paths, and scope the key at the provider.
  • Some tools cannot be brokered. The Copilot CLI cannot change its base URL, so kosa8's copilot kit passes its token in and says so on every run.

06Other options

  • Scoped, short-lived keys with a spending limit at the provider reduce what any leaked key can do, whatever else you use.
  • A secrets manager such as HashiCorp Vault can issue dynamic credentials with a short life; injected as an environment variable, they are still readable by the agent while they last.
  • A model gateway such as LiteLLM can issue virtual keys with budgets, so the agent never holds the provider key; the virtual key still sits in the agent's environment.
  • Claude Code's /sandbox with denyRead, a dev container that mounts no host secrets, Docker Sandboxes, or a full VM in UTM or Lima each keep files outside the agent's view; check how each handles the keys the agent itself needs.
  • Hosted sandboxes such as E2B, Daytona or Modal keep the agent off your machine entirely; your project's secrets then live wherever you upload them.

07Sources

  1. Claude Code: configure permissions (Read deny rules, .claudeignore)
  2. Claude Code: sandboxing (denyRead)
  3. Cursor docs: ignore files
  4. OWASP Top 10 for LLM Applications: LLM01 Prompt Injection
  5. kosa8 docs: Keep API keys out of the sandbox
  6. kosa8 docs: Writing a kit (credentials)
  7. kosa8 docs: The audit log