Security model
The Sandbox SDK is built on Containers, which run each sandbox in its own VM for strong isolation.
Each sandbox runs in a separate VM, providing complete isolation:
- Filesystem isolation - Sandboxes cannot access other sandboxes' files
- Process isolation - Processes in one sandbox cannot see or affect others
- Network isolation - Sandboxes have separate network stacks
- Resource limits - CPU, memory, and disk quotas are enforced per sandbox
For complete security details about the underlying container platform, see Containers architecture.
All code within a single sandbox shares resources:
- Filesystem - All processes see the same files
- Processes - All sessions can see all processes
- Network - Processes can communicate via localhost
For complete isolation, use separate sandboxes per user:
// Good - Each user in separate sandboxconst userSandbox = getSandbox(env.Sandbox, `user-${userId}`);
// Bad - Users sharing one sandboxconst shared = getSandbox(env.Sandbox, 'shared');// Users can read each other's files!
Always validate user input before using it in commands:
// Dangerous - user input directly in commandconst filename = userInput;await sandbox.exec(`cat ${filename}`);// User could input: "file.txt; rm -rf /"
// Safe - validate inputconst filename = userInput.replace(/[^a-zA-Z0-9._-]/g, '');await sandbox.exec(`cat ${filename}`);
// Better - use file APIawait sandbox.writeFile('/tmp/input', userInput);await sandbox.exec('cat /tmp/input');
Sandbox IDs provide basic access control but aren't cryptographically secure. Add application-level authentication:
export default { async fetch(request: Request, env: Env): Promise<Response> { const userId = await authenticate(request); if (!userId) { return new Response('Unauthorized', { status: 401 }); }
// User can only access their sandbox const sandbox = getSandbox(env.Sandbox, userId); return Response.json({ authorized: true }); }};
Preview URLs are public. Add authentication in your service:
from flask import Flask, request, abortimport os
app = Flask(__name__)
def check_auth(): token = request.headers.get('Authorization') if token != f"Bearer {os.environ['AUTH_TOKEN']}": abort(401)
@app.route('/api/data')def get_data(): check_auth() return {'data': 'protected'}
Use environment variables, not hardcoded secrets:
// Bad - hardcoded in fileawait sandbox.writeFile('/workspace/config.js', ` const API_KEY = 'sk_live_abc123';`);
// Good - use environment variablesawait sandbox.startProcess('node app.js', { env: { API_KEY: env.API_KEY, // From Worker environment binding }});
Clean up temporary sensitive data:
try { await sandbox.writeFile('/tmp/sensitive.txt', secretData); await sandbox.exec('python process.py /tmp/sensitive.txt');} finally { await sandbox.deleteFile('/tmp/sensitive.txt');}
- Sandbox-to-sandbox access (VM isolation)
- Resource exhaustion (enforced quotas)
- Container escapes (VM-based isolation)
- Authentication and authorization
- Input validation and sanitization
- Rate limiting
- Application-level security (SQL injection, XSS, etc.)
Use separate sandboxes for isolation:
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
Validate all inputs:
const safe = input.replace(/[^a-zA-Z0-9._-]/g, '');await sandbox.exec(`command ${safe}`);
Use environment variables for secrets:
await sandbox.startProcess('node app.js', { env: { API_KEY: env.API_KEY }});
Clean up temporary resources:
try { const sandbox = getSandbox(env.Sandbox, sessionId); await sandbox.exec('npm test');} finally { await sandbox.destroy();}
- Containers architecture - Underlying platform security
- Sandbox lifecycle - Resource management
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-