How Do I Properly Sandbox Claude Code for Security? A Developer's Guide
I created a virtual machine to sandbox Claude Code. I gave it bypass permissions because I wanted unfettered automation. Within minutes, Claude had accessed Chrome on my host machine.
I thought I’d been hacked.
The “Escape”
Here’s what happened: I spun up a VM, installed Claude Code with full permissions, and let it rip. My first prompt asked Claude to help with a web project. Claude started working, then suddenly opened a browser - my host machine’s browser, with my personal tabs and logged-in sessions.
My heart rate spiked. Had I just let an AI agent escape its sandbox?
I posted about my experience on Reddit, expecting horror stories. Instead, commenters quickly set me straight:
“Claude didn’t escape anything. You gave it a door to walk through.”
The VM had network access to my host. My Chrome browser had the Claude extension installed. The extension accepts localhost connections. Claude simply used the available network path - exactly as designed.
I hadn’t been hacked. I’d misunderstood how isolation works.
Understanding the Real Problem
The issue wasn’t Claude’s behavior - it was my mental model of security boundaries. A VM with network access to the host is not an isolated environment. It’s a connected one.
┌─────────────────────────────────────────┐│ Host Machine ││ ┌───────────────┐ ┌──────────────┐ ││ │ Chrome + │◄───│ VM Network │ ││ │ Claude Ext │ │ Interface │ ││ └───────────────┘ └──────┬───────┘ ││ │ ││ ┌──────────────────────────▼────────┐ ││ │ Virtual Machine │ ││ │ ┌─────────────────────────────┐ │ ││ │ │ Claude Code │ │ ││ │ │ "I see Chrome on localhost!" │ │ ││ │ └─────────────────────────────┘ │ ││ └───────────────────────────────────┘ │└─────────────────────────────────────────┘
This is NOT a sandbox - it's a connected machine!The VM was never isolated. It had a direct line to my host through the network interface. And Chrome’s extension was listening.
What Actually Works
After the Reddit thread and more research, I found three viable approaches. Here’s what I learned.
Option 1: Use Claude Code’s Built-in Sandbox (Start Here)
Claude Code already has sandbox functionality. I didn’t need a VM at all.
{ "allowedTools": ["Read", "Write", "Edit", "Bash"], "dangerouslyDisableSandbox": false}This configuration keeps the sandbox enabled. Claude Code will ask for confirmation before dangerous operations - file writes, shell commands, network requests.
I tested this with a simple project:
$ claude
Claude Code v1.x.xSandbox mode: ENABLED
> Help me refactor this project
I'll read the project files first.[Claude requests permission to read /workspace/src/*]Allow? (y/n)The permission system caught everything. I felt silly for over-engineering with a VM.
Option 2: Docker with Network Isolation (For Stronger Security)
When I need more isolation - like running untrusted code or working with sensitive data - Docker works better than a full VM.
FROM node:20-slim
RUN apt-get update && apt-get install -y git
WORKDIR /workspaceCOPY . .
CMD ["claude"]version: '3.8'services: claude-code: build: . network_mode: none # No network access at all volumes: - ./workspace:/workspace:rw - ~/.claude:/root/.claude:roThe network_mode: none line is critical. It creates true network isolation - no host access, no internet, nothing.
I tested this setup:
# Build and rundocker-compose up -ddocker exec -it claude-code-1 claude
# From inside the container$ ping google.comping: bad address 'google.com'
$ curl localhost:8080curl: (7) Failed to connect to localhost port 8080No network. No Chrome extension access. No escape routes.
For cases where I need limited network access (like npm install), I use an internal network:
version: '3.8'services: claude-code: build: . networks: - claude-net volumes: - ./workspace:/workspace:rw
networks: claude-net: driver: bridge internal: true # No external internet accessThis allows DNS resolution and internal communication but blocks external connections.
Option 3: Permission-Based Security (The Default)
Claude Code’s default behavior is already security-conscious. It asks before:
- Writing files outside allowed directories
- Running shell commands
- Making web requests
I configured allowed directories to limit Claude’s reach:
{ "allowedDirectories": [ "/Users/zhaocaiwen/projects/safe-project" ], "askForConfirmation": true}When Claude tries to access something outside these directories:
I need to write to /etc/hosts to configure local domains.
This is outside allowed directories.Allow? (y/n/always)The key insight: Claude Code is designed to be safe by default. The permission system exists for a reason.
Common Mistakes I Made (And Fixed)
Mistake 1: Assuming VM = Perfect Isolation
I thought a VM was inherently secure. I was wrong. A VM with bridged networking shares the host’s network stack. Any service listening on localhost is accessible.
┌─────────────────────────────────────────────┐│ Host (192.168.1.100) ││ Chrome Extension listening on localhost ││ SSH listening on port 22 ││ Database on port 5432 │└─────────────────────────────────────────────┘ ▲ │ (Same network - can access all services) │┌─────────────────────────────────────────────┐│ VM (192.168.1.101) ││ Claude Code can reach host at 192.168.1.100│└─────────────────────────────────────────────┘Mistake 2: Ignoring the Chrome Extension
I forgot I had the Claude extension installed in Chrome. This extension accepts localhost connections for local development. When Claude reached out, the extension responded.
The fix: Either remove the extension from your host browser, or ensure your sandbox has no network path to the host.
Mistake 3: Using dangerouslyDisableSandbox
The flag name should have been a hint. I disabled the sandbox because I wanted “unfettered” automation. What I got was unfettered access.
{ "dangerouslyDisableSandbox": true // This removes all safety rails}I now use this only when absolutely necessary - and only in completely isolated environments.
Mistake 4: Not Reviewing Generated Code
Claude writes code quickly. I got lazy and stopped reviewing. Bad idea.
> Claude, clean up my project directory
I'll remove the build artifacts...[Claude runs: rm -rf /workspace/build /workspace/dist]
# Wait - did I have important files in dist/?Now I always review, especially for file operations.
When to Use Each Approach
After all this experimentation, here’s my decision tree:
┌─────────────────────────────────────────────────┐│ Do you trust your codebase and dependencies? │└─────────────────────┬───────────────────────────┘ │ ┌───────────┴───────────┐ │ YES │ NO ▼ ▼┌─────────────────────┐ ┌─────────────────────────┐│ Built-in Sandbox │ │ Docker with ││ + Permissions │ │ network_mode: none │└─────────────────────┘ └─────────────────────────┘ │ │ ▼ ▼┌─────────────────────┐ ┌─────────────────────────┐│ Good for: │ │ Good for: ││ - Personal projects │ │ - Untrusted code ││ - Work projects │ │ - Sensitive data ││ - Quick iterations │ │ - Maximum isolation │└─────────────────────┘ └─────────────────────────┘The Takeaway
My “escape” incident wasn’t a security failure - it was an education. Claude Code has built-in security for a reason. Docker provides isolation without VM overhead. And a VM with host network access is just another connected machine.
The best sandbox is one that matches your threat model. For most development work, Claude Code’s permission system is sufficient. For stronger boundaries, use Docker. For maximum paranoia, disconnect the network entirely.
But don’t do what I did: don’t create a “sandbox” with a wide-open door and then act surprised when something walks through it.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments