llm-integration.eu

Claude Code Security: Permissions, Sandbox, Secrets

Claude Code security for enterprise teams: deny rules, the Bash sandbox, .env protection, prompt injection risks and a hardened managed settings baseline.

Updated 12 min readFacts verified on 1 October 2026

TL;DR

Claude Code security rests on four layers: permission rules, the OS-level Bash sandbox, managed settings that developers cannot override, and hooks. Deny rules alone are not a boundary. Since v2.1.283, sessions start in auto mode by default. We recommend a managed baseline that enables the sandbox, blocks .env reads and disables bypass mode.

What does Claude Code security actually protect against?

Claude Code security is about one risk: an agent with shell access acting on instructions you did not give. That covers prompt injection from files, web pages or MCP tools, accidental reads of secrets, and exfiltration over the network. The controls are permissions, sandboxing, managed policy and hooks. None of them is sufficient alone.

Anthropic’s security page describes the baseline. In Manual mode Claude Code starts read-only, asks before edits and before Bash commands that can modify the system, and can only write inside the folder it was started in. Commands that fetch content such as curl and wget are not auto-approved. Web fetches run in a separate context window so a malicious page cannot write straight into the main conversation. New codebases and new MCP servers need a trust confirmation. Anthropic points to a SOC 2 Type 2 report and an ISO 27001 certificate in its Trust Center.

That is the default for a single developer. An enterprise has three extra problems: developers change their own settings, CI runs Claude Code without anyone watching, and the defaults move with every release. The table maps each layer to what it enforces.

Layer What it enforces What it does not cover
Permission rules Which tools, files and domains Claude may use Programs invoked in a different form, e.g. sh -c
Bash sandbox OS-level file and network limits for shell commands and child processes Read, Edit and Write tools; native Windows
Managed settings Policy that user, project and CLI flags cannot override Machines your MDM does not reach
Hooks Custom checks before or after a tool call Anything you did not script
Dev container A disposable machine boundary Data you mount into the container

GDPR Article 32 asks for technical measures appropriate to the risk and for regular testing of their effectiveness. For a coding agent with shell access, we read that as: an enforced sandbox, a written policy file, and a review cycle. Where your Claude traffic itself is processed is a separate question, covered in our Claude data privacy guide.

How do permission modes and allow and deny rules work?

Claude Code evaluates permission rules in a fixed order: deny first, then ask, then allow. The first match wins, and specificity does not matter. A broad deny like Bash(aws *) beats a narrow allow like Bash(aws s3 ls). A deny set in managed settings cannot be lifted by user settings, project settings or --allowedTools.

The permissions reference lists six modes. default (shown as Manual) prompts on first use of each tool. acceptEdits approves file edits and common filesystem commands in the working directory. plan reads and proposes without editing. auto runs without routine prompts while a classifier model reviews actions. dontAsk denies anything that would prompt. bypassPermissions skips prompts and the docs say to use it only in isolated containers or VMs.

The change most security teams have missed: from v2.1.283, auto mode is the built-in starting mode for interactive terminal and VS Code sessions on every plan and provider, Bedrock and Google Cloud included. The permission modes page also says claude -p on a third-party provider starts in auto from v2.1.285. On Bedrock, Google Cloud and Foundry, auto mode needs Sonnet 5 or later, Opus 4.7 or later, or a Fable model, and each classifier check counts toward your token bill. Anthropic’s own warning: auto mode reduces prompts but does not guarantee safety.

The second trap is what a Bash rule matches. It matches the command text Claude writes, not the program.

Deny rule Stops Does not stop
Bash(curl *) curl https://example.com /usr/bin/curl ..., sh -c 'curl ...'
Bash(rm *) rm -rf build/ /bin/rm -rf build/, bash -c 'rm -rf build/'
Bash(git push *) git push origin main git -C . push origin main

So treat Bash deny rules as guardrails for normal behaviour, not as a security boundary. The enforcement layer for files and network is the sandbox. Project allow rules from a repository’s .claude/settings.json only apply after a developer accepts the workspace trust dialog. Deny and ask rules apply immediately, because they only restrict.

How do you sandbox Bash and keep secrets out of reach?

The sandbox enforces file and network limits at the operating system level for every shell command and its child processes. It uses Seatbelt on macOS and bubblewrap plus socat on Linux and WSL2. Native Windows is not supported. Enable it, make it mandatory, list your credential paths, and block .env reads separately for the file tools.

The default read policy surprises people. The sandboxing docs state that sandboxed commands can read the entire computer by default, including ~/.aws/credentials and ~/.ssh/. There is no built-in credential deny list: only the files and variables you list in sandbox.credentials are protected. Writes are limited to the working directory, added directories and a per-user temp directory.

Three settings turn the sandbox from a convenience into a control:

  1. sandbox.failIfUnavailable: true stops Claude Code from starting when bubblewrap is missing, instead of warning and running unsandboxed.
  2. sandbox.allowUnsandboxedCommands: false removes the escape hatch where Claude retries a blocked command outside the sandbox.
  3. sandbox.network.allowManagedDomainsOnly: true honours only the domain allowlist from managed settings and blocks everything else without prompting.

Know the limits. The built-in proxy decides by hostname and does not inspect TLS by default. Anthropic warns that allowing broad domains such as github.com can open exfiltration paths, including domain fronting. excludedCommands has no managed-only lock. Since v2.1.282, entries from project and local settings are ignored once managed settings set allowUnsandboxedCommands: false, but a developer’s own user settings can still add commands that run outside the sandbox. docker is incompatible and usually ends up excluded.

For secrets, combine two mechanisms. permissions.deny rules such as Read(.env) hide matching files from search, block reads and also block Edit and Write on those paths. They also catch cat, head and redirections. The settings reference is explicit that they do not catch grep -r pattern . or arbitrary subprocesses, which is why the sandbox’s denyRead and credentials entries matter. Third, set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1. It strips Anthropic and cloud credentials from the environment of Bash, hooks and MCP stdio servers, while the parent process keeps them for API calls. On Bedrock this keeps the AWS session that pays for tokens out of reach of a shell expansion.

How does Claude Code handle prompt injection?

Claude Code reduces prompt injection with approval prompts, a separate context window for web fetches, trust dialogs for new repositories and MCP servers, and detection of suspicious Bash commands. In auto mode the classifier also blocks actions that appear driven by hostile content. None of these stops an attacker who already controls the repository’s configuration in an unattended run.

The highest-risk case is CI. Trust verification is disabled with -p. The permissions page lists what a repository can supply in a claude -p or SDK run in a folder that was never trusted: hooks in settings files, the env block, helper commands, and servers in .mcp.json are all used without asking. A pull request that adds a hook therefore runs code on your runner. For pipelines that touch untrusted code, the docs give three mitigations: --setting-sources user so project settings and .mcp.json are not read, --bare so no project hooks, skills or MCP servers load, and --settings '{"disableAllHooks": true}'.

Hooks are also your own enforcement tool. A PreToolUse hook receives the tool input as JSON and can deny the call. Exit code 2 blocks the call even if the hook’s JSON says allow, and a deny rule still wins over any hook decision. A ConfigChange hook can block changes to user, project and local settings mid-session, but not managed policy changes, which always apply. Set allowManagedHooksOnly: true so only hooks from managed settings or force-enabled plugins run. One side effect: /goal no longer works.

MCP needs its own rule. Anthropic reviews connectors in its directory against listing criteria but does not security-audit any MCP server. Use allowManagedMcpServersOnly with an allowlist. For agents that run with --dangerously-skip-permissions, use the dev container. Its reference setup runs as a non-root user and ships an init-firewall.sh egress allowlist. Anthropic notes that even then a malicious project can exfiltrate anything inside the container, including Claude Code credentials in ~/.claude.

What does a hardened managed settings baseline look like?

Put the policy in managed-settings.json and deliver it through MDM or a file in the system directory. Managed settings sit at the top of the precedence chain. The baseline below enforces the sandbox, protects secrets, disables bypass mode and restricts hooks. Adjust the domain list and credential paths to your environment before rollout.

{
  "permissions": {
    "defaultMode": "default",
    "disableBypassPermissionsMode": "disable",
    "blockReadsOutsideWorkingDirectories": true,
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(./secrets/**)",
      "Read(~/.aws/**)",
      "Read(~/.ssh/**)",
      "Bash(curl *)",
      "Bash(wget *)"
    ],
    "ask": ["Bash(git push *)"]
  },
  "allowManagedHooksOnly": true,
  "allowManagedMcpServersOnly": true,
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "credentials": {
      "files": [
        { "path": "~/.aws/credentials", "mode": "deny" },
        { "path": "~/.ssh", "mode": "deny" }
      ],
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    },
    "network": {
      "allowManagedDomainsOnly": true,
      "allowedDomains": ["git.example.internal", "registry.npmjs.org"]
    }
  },
  "env": { "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB": "1" },
  "cleanupPeriodDays": 14
}

defaultMode: "default" starts sessions in Manual instead of auto. Developers can still switch to auto. If your policy forbids the classifier entirely, add "disableAutoMode": "disable" under permissions. cleanupPeriodDays: 14 is our choice. The default keeps local transcripts in plaintext under ~/.claude/projects/ for 30 days. blockReadsOutsideWorkingDirectories requires v2.1.257 or later.

Roll it out in this order:

  1. Place the file at /Library/Application Support/ClaudeCode/managed-settings.json (macOS), /etc/claude-code/managed-settings.json (Linux and WSL) or C:\Program Files\ClaudeCode\managed-settings.json (Windows).
  2. Run /status on a test machine. The Setting sources line must show Enterprise managed settings (file).
  3. Run a normal build and test cycle inside Claude Code and add the hosts it needs to allowedDomains. Expect docker and some Go-based CLIs to need excludedCommands.
  4. Roll out to a pilot team, then the fleet. MDM profiles are re-read every 30 minutes, files when they change.

Two points for Bedrock and Google Cloud users. First, server-managed settings from the claude.ai console do not reach sessions that export a CLAUDE_CODE_USE_* provider variable. Use MDM or the file, or a self-hosted Claude apps gateway. Second, the data usage page says metrics, error reports and /feedback uploads are off by default on Bedrock and Google Cloud, while session surveys and the WebFetch domain check still run. CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC and skipWebFetchPreflight turn those off. Delivery mechanics and OpenTelemetry deserve their own rollout plan. The EU routing itself is in our Claude Code Bedrock EU setup.

Run it yourself or outsource?

Running Claude Code security in-house means owning a policy file that changes with the product. The changelog lists 27 releases dated September 2026 alone, several of which changed permission or sandbox behaviour. Someone has to read them, test the baseline, and ship updates through MDM. Outsourcing makes sense when nobody on your side owns endpoint policy.

The recurring work, as we see it:

Task Frequency Typical owner
Read release notes, flag permission and sandbox changes Weekly Platform or security engineer
Re-test the baseline on macOS, Linux, WSL2 Per relevant release Platform engineer
Review domain allowlist and excludedCommands requests Weekly Security engineer
Check policy is in force (/status, claude doctor) Monthly sample IT or MDM team
Incident handling: leaked key, suspicious agent action On demand Security team, DPO
Review CI pipelines that run claude -p Per new pipeline DevOps

Our estimate, not a sourced figure: for 50 to 200 developers, this is two to four hours a week of a security-minded platform engineer, plus incident time. Incidents carry a hard deadline. If an agent leaks personal data, GDPR Article 33 asks for notification to the supervisory authority where feasible within 72 hours.

Keep it in-house if you already run MDM, have an endpoint security team, and keep policy as code in a repository. That team will be faster than any external party. Outsource to a managed service provider when you have no MDM coverage for developer machines, no one to triage release notes, or when Claude Code runs in many unattended pipelines.

If you outsource, demand this in the contract:

  • SLA: a written deadline for assessing each Claude Code release and for shipping policy fixes after a security-relevant change.
  • DPA under Article 28 GDPR: the provider sees transcripts, logs and possibly code during incidents. That is processing on your behalf.
  • Subprocessor list: who else touches your logs or telemetry, and where.
  • Access model: changes go through your MDM with your approval, no standing admin access to developer machines.
  • Exit: the policy stays in your repository as plain JSON, with change history, so you can take it back without rebuilding.

Cost of the tooling for each seat is in our Claude Code pricing guide for teams.

FAQ

Is Claude Code safe to use on company code?

It can be, with an enforced policy. Out of the box, sessions now start in auto mode and sandboxed commands can read the whole disk. With managed settings that enable the sandbox, deny .env and credential reads, and disable bypass mode, the main risks are covered. Review remains your responsibility.

Does a deny rule for .env stop every read?

No. Read(.env) blocks Claude’s file tools and recognised commands like cat, but not grep -r or arbitrary subprocesses. Add the same paths to sandbox.filesystem.denyRead or sandbox.credentials, and enable the sandbox so the operating system enforces the block.

What does hardening Claude Code cost?

The settings are free. The costs are people and tokens. In auto mode on Bedrock, Google Cloud or Foundry, every classifier check counts toward your token usage. We estimate two to four engineer hours a week for policy upkeep at 50 to 200 developers. That figure is ours, not Anthropic’s.

Sandbox vs dev container: which do we need?

Use the sandbox on developer laptops. It limits shell commands without changing how people work. Use a dev container when Claude runs unattended or with --dangerously-skip-permissions. The container is the stronger boundary, but anything mounted into it can still be exfiltrated.

Does Claude Code send telemetry when we use Bedrock?

By default, no metrics, error reports or feedback uploads. Session quality surveys and the WebFetch domain safety check still run, sending only the hostname for the check. Set CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC and skipWebFetchPreflight: true to turn both off.

Can developers override managed settings?

Not for permission rules or boolean sandbox keys. Managed settings have the highest precedence, including over command-line flags. Array keys like excludedCommands merge from all scopes, so developers can widen them. Use allowManagedDomainsOnly and allowManagedReadPathsOnly to lock the network and read lists.

Sources

  1. Claude Code docs: Security (1 October 2026)
  2. Claude Code docs: Configure permissions (1 October 2026)
  3. Claude Code docs: Permission modes (1 October 2026)
  4. Claude Code docs: Configure the sandboxed Bash tool (1 October 2026)
  5. Claude Code docs: Settings reference (1 October 2026)
  6. Claude Code docs: Environment variables (1 October 2026)
  7. Claude Code docs: Hooks reference (1 October 2026)
  8. Claude Code docs: Deploy managed settings (1 October 2026)
  9. Claude Code docs: Server-managed settings (1 October 2026)
  10. Claude Code docs: Data usage (1 October 2026)
  11. Claude Code docs: Development containers (1 October 2026)
  12. Claude Code changelog (1 October 2026)
  13. GDPR (Regulation (EU) 2016/679), EUR-Lex (1 October 2026)

Related guides