Improper Isolation or Compartmentalization
Description
Improper Isolation or Compartmentalization occurs when an application fails to properly separate components, processes, or data that should be isolated from each other. This includes running components with more privileges than needed, failing to use sandboxes or containers, sharing resources between security domains, and not implementing defense in depth. A compromise of one component leads to compromise of others.
Risk
Compromise of one component leads to full system compromise. Privilege escalation from low-privilege to high-privilege components. Cross-tenant data access in multi-tenant systems. Lateral movement through shared resources. Sensitive data exposed through improper boundaries. Malware spread through uncontained processes.
Solution
Implement proper process isolation using containers or sandboxes. Apply principle of least privilege. Use separate databases/schemas per tenant. Implement network segmentation. Use capability-based security. Separate authentication from business logic. Apply defense in depth across all layers.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Data Breach Data from one domain accessed by another. |
| Integrity | Scope: Cross-Contamination One component corrupts another's data. |
| Availability | Scope: Cascade Failure One failure brings down entire system. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: No isolation between components
public class VulnerableMonolithicApp {
// All components share same process and privileges
private UserService userService;
private PaymentService paymentService;
private AdminService adminService;
private FileService fileService;
// VULNERABLE: Payment service can access admin functions
public void processPayment(PaymentRequest request) {
// Payment code runs with full application privileges
// Can access userService, adminService, fileService
paymentService.process(request);
// If payment code is compromised, attacker has access to everything
}
// VULNERABLE: Single database, all services share access
private DataSource sharedDatabase; // Everyone uses same connection
public void storeData(String component, String data) {
// All components write to same tables
// No row-level security
sharedDatabase.execute("INSERT INTO data ...");
}
}
// VULNERABLE: Multi-tenant without isolation
public class VulnerableMultiTenantService {
private DataSource db; // Single shared database
public List<Order> getOrders(String tenantId) {
// VULNERABLE: Relies only on WHERE clause for isolation
// SQL injection or bug could expose other tenants' data
return db.query(
"SELECT * FROM orders WHERE tenant_id = ?",
tenantId
);
}
// VULNERABLE: Shared file storage
public void saveFile(String tenantId, String filename, byte[] data) {
// All tenants share same directory
// Path traversal could access other tenants' files
Path path = Paths.get("/data/uploads/" + filename);
Files.write(path, data);
}
}
# VULNERABLE: Python without process isolation
import os
from flask import Flask, request
app = Flask(__name__)
# VULNERABLE: All code runs in same process with same privileges
class MonolithicApp:
def __init__(self):
# Single database connection shared everywhere
self.db = Database()
# No separation of concerns
self.user_service = UserService(self.db)
self.payment_service = PaymentService(self.db)
self.admin_service = AdminService(self.db)
# VULNERABLE: Payment processing can access admin DB
def process_payment(self, payment_data):
# If this is exploited, attacker can access admin functions
result = self.payment_service.process(payment_data)
# Same process, can call admin functions
self.admin_service.do_something()
# VULNERABLE: Shell commands executed without sandboxing
@app.route('/convert', methods=['POST'])
def convert_file_vulnerable():
filename = request.form['filename']
# VULNERABLE: Executes in main process context
# Can access all application files and resources
os.system(f"convert {filename} output.pdf")
return "Converted"
# VULNERABLE: Multi-tenant with shared resources
class VulnerableMultiTenant:
def __init__(self):
self.shared_db = Database()
self.shared_cache = RedisCache()
self.shared_storage = FileStorage('/data')
def get_tenant_data(self, tenant_id, query):
# VULNERABLE: Only application-level filtering
# Database doesn't enforce isolation
return self.shared_db.execute(
f"SELECT * FROM data WHERE tenant_id = '{tenant_id}' AND {query}"
)
// VULNERABLE: Node.js monolithic without isolation
const express = require('express');
const app = express();
// VULNERABLE: All services in same process
class MonolithicServer {
constructor() {
this.db = new Database();
// Everything shares same resources
this.userService = new UserService(this.db);
this.paymentService = new PaymentService(this.db);
this.fileService = new FileService(this.db);
}
// VULNERABLE: Code execution without sandboxing
executeUserCode(code) {
// Runs in same context as application
// Can access all global objects
return eval(code);
}
}
// VULNERABLE: Multi-tenant without database isolation
class MultiTenantService {
constructor() {
this.pool = new DatabasePool(); // Shared pool
}
async getData(tenantId, table) {
// VULNERABLE: Tenant isolation only through queries
// Not enforced at database level
return await this.pool.query(
`SELECT * FROM ${table} WHERE tenant_id = $1`,
[tenantId]
);
}
// VULNERABLE: Shared secrets manager
async getSecret(tenantId, secretName) {
// All tenants use same secrets store
// Naming convention is only protection
return await secretsManager.get(`${tenantId}/${secretName}`);
}
}
// VULNERABLE: Go without proper isolation
package main
// VULNERABLE: Monolithic design
type Application struct {
db *sql.DB // Single shared database
userService *UserService
adminService *AdminService
config *Config // Shared config with all secrets
}
// VULNERABLE: All requests handled in same process
func (app *Application) handleRequest(w http.ResponseWriter, r *http.Request) {
// User-facing code can access admin service
// No process boundary
app.userService.HandleUser(w, r)
}
// VULNERABLE: Executing untrusted code
func (app *Application) runPlugin(pluginCode string) {
// Runs with full application privileges
// No sandboxing
plugin := LoadPlugin(pluginCode)
plugin.Execute(app) // Plugin has access to everything
}
// VULNERABLE: Multi-tenant shared resources
func (app *Application) getTenantData(tenantID string) []Data {
// No database-level tenant isolation
rows, _ := app.db.Query(
"SELECT * FROM data WHERE tenant_id = $1",
tenantID,
)
return parseRows(rows)
}
Fixed Code
// SAFE: Microservices with proper isolation
public class IsolatedPaymentService {
// SAFE: Dedicated database with limited access
private final DataSource paymentDatabase;
// SAFE: Isolated configuration
private final PaymentConfig config;
public IsolatedPaymentService() {
// Only payment-related database access
this.paymentDatabase = DataSourceBuilder.create()
.url(System.getenv("PAYMENT_DB_URL"))
.username(System.getenv("PAYMENT_DB_USER"))
.build();
// Limited configuration
this.config = new PaymentConfig();
}
// SAFE: Can only perform payment operations
public PaymentResult process(PaymentRequest request) {
// No access to user service, admin service, etc.
return processPayment(request);
}
}
// SAFE: Multi-tenant with database-level isolation
public class IsolatedMultiTenantService {
private final Map<String, DataSource> tenantDatabases;
// SAFE: Separate database per tenant
public DataSource getTenantDatabase(String tenantId) {
return tenantDatabases.computeIfAbsent(tenantId, id -> {
// Each tenant gets their own database
return DataSourceBuilder.create()
.url(String.format("jdbc:postgresql://db/%s_db", id))
.build();
});
}
public List<Order> getOrders(String tenantId) {
// SAFE: Uses tenant-specific database
DataSource ds = getTenantDatabase(tenantId);
// Even SQL injection can't access other tenants
return ds.query("SELECT * FROM orders");
}
// SAFE: Isolated file storage per tenant
public void saveFile(String tenantId, String filename, byte[] data) {
// Validate filename
if (!isValidFilename(filename)) {
throw new SecurityException("Invalid filename");
}
// Each tenant has separate storage
Path tenantDir = Paths.get("/data/tenants", tenantId, "files");
Path filePath = tenantDir.resolve(filename).normalize();
// Verify within tenant directory
if (!filePath.startsWith(tenantDir)) {
throw new SecurityException("Path traversal detected");
}
Files.write(filePath, data);
}
}
// SAFE: Using containers for process isolation
@Configuration
public class IsolatedProcessConfig {
@Bean
public CodeExecutor sandboxedExecutor() {
// Execute untrusted code in isolated container
return new DockerExecutor(
DockerConfig.builder()
.image("sandbox:latest")
.memoryLimit("256m")
.cpuLimit(0.5)
.networkMode("none") // No network access
.readOnlyRootFilesystem(true)
.build()
);
}
}
# SAFE: Python with proper isolation
import os
from multiprocessing import Process
import docker
# SAFE: Microservice with limited scope
class IsolatedPaymentService:
def __init__(self):
# Only payment-specific database
self.db = Database(os.environ['PAYMENT_DB_URL'])
# No access to other services' resources
def process_payment(self, payment_data):
# Can only perform payment operations
return self.db.execute_payment(payment_data)
# SAFE: Sandboxed code execution
class SandboxedExecutor:
def __init__(self):
self.docker_client = docker.from_env()
def execute_untrusted_code(self, code, timeout=30):
# Execute in isolated container
container = self.docker_client.containers.run(
image='python-sandbox:latest',
command=['python', '-c', code],
detach=True,
mem_limit='256m',
cpu_period=100000,
cpu_quota=50000, # 50% CPU
network_mode='none', # No network
read_only=True,
security_opt=['no-new-privileges'],
cap_drop=['ALL']
)
try:
result = container.wait(timeout=timeout)
logs = container.logs()
return logs.decode()
finally:
container.remove(force=True)
# SAFE: Multi-tenant with database isolation
class IsolatedMultiTenant:
def __init__(self):
self.tenant_dbs = {}
self.tenant_storage = {}
def get_tenant_db(self, tenant_id):
if tenant_id not in self.tenant_dbs:
# Each tenant gets separate database
self.tenant_dbs[tenant_id] = Database(
f"postgresql://db/{tenant_id}_db"
)
return self.tenant_dbs[tenant_id]
def get_tenant_data(self, tenant_id, query):
# Uses tenant-specific database
db = self.get_tenant_db(tenant_id)
# Can't access other tenants' data even with SQL injection
return db.execute(query)
def get_tenant_storage(self, tenant_id):
# Isolated storage per tenant
tenant_path = f"/data/tenants/{tenant_id}"
if tenant_id not in self.tenant_storage:
# Create chroot-like isolation
self.tenant_storage[tenant_id] = IsolatedStorage(tenant_path)
return self.tenant_storage[tenant_id]
# SAFE: Process isolation for sensitive operations
class SecureProcessor:
def process_sensitive_data(self, data):
# Run in separate process with limited privileges
def isolated_work(input_queue, output_queue):
# Drop privileges
os.setgid(65534) # nobody
os.setuid(65534)
# Process data
result = do_processing(input_queue.get())
output_queue.put(result)
from multiprocessing import Queue
input_q, output_q = Queue(), Queue()
input_q.put(data)
p = Process(target=isolated_work, args=(input_q, output_q))
p.start()
p.join(timeout=30)
return output_q.get()
// SAFE: Node.js with process isolation
const { Worker } = require('worker_threads');
const { VM } = require('vm2');
// SAFE: Sandboxed code execution
class SandboxedExecutor {
executeUntrustedCode(code, timeout = 5000) {
// Use VM2 for sandboxed execution
const vm = new VM({
timeout,
sandbox: {
// Only expose safe APIs
console: {
log: (...args) => console.log('[sandbox]', ...args)
}
},
eval: false,
wasm: false
});
try {
return vm.run(code);
} catch (error) {
return { error: error.message };
}
}
// SAFE: Execute in separate worker thread
async executeInWorker(code) {
return new Promise((resolve, reject) => {
const worker = new Worker(`
const { parentPort } = require('worker_threads');
try {
const result = eval(${JSON.stringify(code)});
parentPort.postMessage({ result });
} catch (error) {
parentPort.postMessage({ error: error.message });
}
`, { eval: true });
const timeout = setTimeout(() => {
worker.terminate();
reject(new Error('Execution timeout'));
}, 5000);
worker.on('message', (msg) => {
clearTimeout(timeout);
resolve(msg);
});
});
}
}
// SAFE: Multi-tenant with proper isolation
class IsolatedMultiTenantService {
constructor() {
this.tenantConnections = new Map();
}
getTenantConnection(tenantId) {
if (!this.tenantConnections.has(tenantId)) {
// Each tenant gets dedicated database
const connection = new Database({
host: 'db',
database: `tenant_${tenantId}`,
user: `tenant_${tenantId}_user`,
// Tenant-specific credentials
password: process.env[`TENANT_${tenantId}_DB_PASS`]
});
this.tenantConnections.set(tenantId, connection);
}
return this.tenantConnections.get(tenantId);
}
async getData(tenantId, query) {
// Uses tenant-specific database
const db = this.getTenantConnection(tenantId);
return await db.query(query);
}
}
Exploited in the Wild
Container Escapes
Lack of proper container isolation exploited for host access.
Multi-Tenant Data Breaches
Shared database access leading to cross-tenant exposure.
Lateral Movement
Compromise of one component leading to full system breach.
Tools to test/exploit
-
Container security scanners.
-
Lateral movement detection tools.
-
Multi-tenant isolation testing.
CVE Examples
-
CVE-2020-15257: Container escape via shared namespaces.
-
Cloud multi-tenancy isolation failures.
References
-
MITRE. "CWE-653: Improper Isolation or Compartmentalization." https://cwe.mitre.org/data/definitions/653.html
-
NIST. "Security Isolation Guidelines."