CWE-668: Exposure of Resource to Wrong Sphere
Overview
Exposing resources (files, database connections, memory) to wrong sphere of control occurs when internal resources become accessible outside intended boundaries (process, user, security context), enabling unauthorized access, resource exhaustion, and data leakage.
OWASP Classification
A01:2025 - Broken Access Control
Risk
High: Wrong sphere exposure enables file descriptor leaks (access others' files), memory exposure (shared across security boundaries), database connection leaks (access others' data), socket hijacking, and privilege confusion.
Remediation Strategy
Properly Scope Resources (Primary Defense)
# VULNERABLE - global file handle
log_file = open('/var/log/app.log', 'a') # Global scope
def process_request(user_id):
log_file.write(f"User {user_id}\n") # All users share same handle
# File handle leaked across security boundaries
# SECURE - proper scoping
def process_request(user_id):
with open('/var/log/app.log', 'a') as log_file:
log_file.write(f"User {user_id}\n")
# File closed, not accessible outside function
Close File Descriptors After fork()
// VULNERABLE - file descriptors inherited
int fd = open("/etc/secrets", O_RDONLY);
if (fork() == 0) {
// Child process inherits fd!
// Can read parent's file
execve("/bin/untrusted_program", ...);
}
// SECURE - close before exec or use O_CLOEXEC
int fd = open("/etc/secrets", O_RDONLY | O_CLOEXEC);
// File descriptor automatically closed on exec
// Or explicitly close
if (fork() == 0) {
close(fd); // Close in child
execve("/bin/untrusted_program", ...);
}
Use Thread-Local Storage
// VULNERABLE - shared across threads
public class RequestHandler {
private static User currentUser; // Shared!
public void handleRequest(Request req) {
currentUser = authenticate(req);
// Another thread can modify currentUser!
processRequest();
}
}
// SECURE - thread-local
public class RequestHandler {
private static ThreadLocal<User> currentUser = new ThreadLocal<>();
public void handleRequest(Request req) {
currentUser.set(authenticate(req));
// Each thread has own copy
processRequest();
currentUser.remove(); // Clean up
}
}
Isolate Database Connections
# VULNERABLE - connection pooling issue
global_connection = get_db_connection()
def query_user_data(user_id):
cursor = global_connection.cursor()
cursor.execute(f"SELECT * FROM data WHERE user_id = {user_id}")
return cursor.fetchall()
# Connection state shared across users!
# SECURE - connection per request/transaction
def query_user_data(user_id):
with get_db_connection() as conn:
with conn.cursor() as cursor:
cursor.execute(
"SELECT * FROM data WHERE user_id = %s",
(user_id,)
)
return cursor.fetchall()
# Connection returned to pool, cursor closed
Remediation Steps
Core principle: Never expose resources outside their intended trust sphere; resource visibility and accessibility must be explicitly defined and enforced by design, not inferred from request context.
- Identify exposed resources
- Determine wrong sphere
- Trace resource sharing
- Properly scope resources: Use local variables, context managers, request-scoped objects instead of global/static resources
- Isolate by security context: Use thread-local storage, connection-per-request, close file descriptors in child processes
- Test cross-boundary access: Verify User A cannot access User B's resources, child processes don't inherit sensitive file descriptors
- Add resource cleanup: Ensure resources are closed/cleared when crossing security boundaries (connection pool return, thread cleanup)
Common Vulnerable Patterns
File Descriptor Leaks
// Descriptors inherited by child processes
int fd = socket(AF_INET, SOCK_STREAM, 0);
fork(); // Child inherits socket!
exec("/bin/sh"); // Shell has access to socket
Why is this vulnerable: When a process forks, the child inherits all open file descriptors from the parent. If the parent then calls exec() to run another program (like a shell or untrusted application), that new program inherits the socket and can read/write to it, potentially intercepting network traffic or accessing resources meant only for the parent process. This enables privilege escalation and data leakage across security boundaries.
Shared Memory Exposure
// Memory shared across processes
int shmid = shmget(key, size, IPC_CREAT | 0666);
char *shared = shmat(shmid, NULL, 0);
strcpy(shared, sensitive_data);
// Other processes can attach and read!
Why is this vulnerable: The permissions 0666 (read/write for all users) allow any process on the system to attach to this shared memory segment and read the sensitive data. Shared memory persists until explicitly removed, so even after the creating process terminates, the data remains accessible. An attacker can enumerate shared memory segments using ipcs and attach to segments with weak permissions, bypassing process isolation.
Global Variables in Web Applications
current_user = None # Global
@app.route('/process')
def process():
global current_user
current_user = get_authenticated_user()
# Another request can overwrite current_user!
do_something_with_current_user()
Why is this vulnerable: In multi-threaded web servers, multiple HTTP requests are processed concurrently by different threads sharing the same global variables. When Request A sets current_user to Alice and Request B simultaneously sets it to Bob, Request A's code may end up using Bob's identity due to race conditions. This causes authentication bypass, privilege escalation, and data leakage - users can access each other's data unpredictably.
Session Fixation
<?php
// Session ID exposed to wrong user
session_start();
if (!isset($_SESSION['user_id'])) {
$_SESSION['user_id'] = $_GET['user']; // Attacker-controlled!
}
Why is this vulnerable: Accepting a session identifier from user input ($_GET['user']) allows attackers to force victims to use a session ID the attacker controls. The attacker sends a victim a link with ?user=attacker_chosen_id, and when the victim authenticates, their authentication is associated with the attacker's chosen session. The attacker can then use that session ID to access the victim's account without knowing their password.
Secure Patterns
Context Managers for Resource Scoping
from contextlib import contextmanager
@contextmanager
def get_user_context(user_id):
context = UserContext(user_id)
try:
yield context
finally:
context.cleanup()
with get_user_context(user_id) as ctx:
ctx.process()
Why this works: Context managers enforce automatic resource cleanup through Python's __enter__/__exit__ protocol - the UserContext is created when entering the with block, yielded to the caller, and automatically cleaned up in the finally block regardless of whether the code succeeds, fails, or raises an exception. This prevents resource leakage across security boundaries - if ctx.process() throws an exception or returns early, context.cleanup() still runs, ensuring resources (file handles, database connections, memory) don't persist beyond the intended scope. Scoped resources are isolated to individual requests/users - each with block gets a fresh context, preventing User A's context from leaking to User B's request in multi-threaded web servers. The finally block guarantees cleanup even during unexpected control flow (exceptions, early returns, continue/break), implementing fail-safe resource management. Explicit scope boundaries (with ... as ctx:) make it obvious to code reviewers where resources are acquired and released, unlike global variables where lifecycle is unclear.
Process Isolation
import subprocess
result = subprocess.run(
['/usr/bin/sandbox', 'untrusted_script.py'],
stdin=subprocess.DEVNULL,
capture_output=True,
timeout=5
)
Why this works: Process isolation creates a security boundary - the untrusted script runs in a completely separate process with its own memory space, file descriptors, and security context, unable to access the parent process's resources. stdin=subprocess.DEVNULL prevents input injection - the child process cannot read from the parent's stdin or trick the user into providing credentials. capture_output=True isolates stdout/stderr - the untrusted code's output is captured in memory rather than written to the parent's terminal, preventing terminal escape sequence attacks or information leakage. timeout=5 enforces resource limits - the child process is forcibly killed after 5 seconds, preventing denial-of-service through infinite loops or CPU exhaustion. File descriptors are not inherited by default in subprocess.run() - unlike fork() which duplicates all file descriptors, subprocess creates a fresh process that doesn't inherit the parent's open files, sockets, or database connections, preventing the untrusted code from reading sensitive data or hijacking network connections. /usr/bin/sandbox wrapper can further restrict the child via seccomp, AppArmor, SELinux, chroot, or containers.
Request-Scoped Resources (Java)
@RequestScoped
public class UserSession {
private User currentUser;
@PostConstruct
public void init() {
// Initialized per request
}
@PreDestroy
public void cleanup() {
// Cleaned up after request
}
}
Why this works: @RequestScoped ensures each HTTP request gets a fresh instance of UserSession - when Request A arrives, the CDI (Context and Dependency Injection) container creates a new UserSession, and when Request A completes, that instance is destroyed and cannot be accessed by Request B. This prevents session fixation and cross-user data leakage in multi-threaded web servers where many requests are processed concurrently - without request scoping, a singleton UserSession would be shared across all requests, causing User A's authentication to be overwritten by User B's login. @PostConstruct init() runs after dependency injection but before the first business method call, providing a hook to initialize request-specific resources (database connections, authentication tokens). @PreDestroy cleanup() runs automatically when the request completes (either normally or via exception), ensuring resources are released even if developers forget explicit cleanup code - connections are returned to pools, temporary files are deleted, and security contexts are cleared. Thread-safe by design - each thread handling a request gets its own UserSession instance, eliminating the need for synchronization locks.
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code