CWE-668: Exposure of Resource to Wrong Sphere
Overview
Exposure of resource to wrong sphere occurs when an internal resource - a file handle, a block of memory, a database connection, a piece of request or session state - becomes reachable from another process, another user's request, or another security context. It usually happens by accident: a resource is put in a wider scope than it needs, or is not released or isolated when crossing a boundary such as a fork(), a new thread, or a new request.
CWE-668 is a Class, and MITRE marks it discouraged for direct vulnerability mapping - its own rationale is that it "is high-level and is often misused as a catch-all when lower-level children might be applicable". The sections below cover what the family has in common and the two shapes that recur across it; fix the finding on the child page that names the mechanism.
Relationship to Other CWEs
This page is a router. CWE-668 sits under CWE-664 (Improper Control of a Resource Through its Lifetime) in MITRE's Research Concepts view, and it has close to thirty children spanning every kind of resource. Nine of them have their own page here, and the primary defense differs enough between them that the fix belongs on the child page. The entries below are this page and those nine:
- CWE-668 (this page) - an internal resource becomes reachable from another process, another user's request, or another security context
- CWE-200 - Exposure of Sensitive Information to an Unauthorized Actor (itself a router for the information-exposure family)
- CWE-402 - Transmission of Private Resources into a New Sphere (a handle inherited across
fork/exec, or left open for a successor) - CWE-377 - Insecure Temporary File
- CWE-427 - Uncontrolled Search Path Element
- CWE-522 - Insufficiently Protected Credentials
- CWE-642 - External Control of Critical State Data
- CWE-732 - Incorrect Permission Assignment for Critical Resource
- CWE-498 - Cloneable Class Containing Sensitive Information
- CWE-134 - Use of Externally-Controlled Format String
The child this page's first pattern describes has no page here yet: CWE-488 (Exposure of Data Element to Wrong Session), per-request state held in a singleton, a servlet member field, or a module-level variable and read by a concurrent request belonging to somebody else. Use CWE-488 for that finding rather than CWE-668.
Use this page when a finding arrives labelled CWE-668 with no more specific weakness attached, or as background for the two shapes the children have in common: wrong scope, and an unreleased boundary crossing.
OWASP Classification
A01:2025 - Broken Access Control
Risk
High: Wrong-sphere exposure lets one user or process read or modify state that belongs to another - file descriptors inherited by a spawned child process, shared memory or IPC objects left world-readable, or a global variable overwritten by a concurrent request. A request then carries out its work as the wrong user, or a child process reaches a file its own privileges would not have opened.
Remediation Steps
Core Principle: Keep a resource inside the security boundary it belongs to. Its visibility and lifetime should be set deliberately rather than inherited from whatever scope it was declared in.
Trace the Data Path
- Source: A resource acquired in a scope wider than it needs - a global or static variable, a handle opened before a
fork(), memory allocated with broad permissions. - Sink: Any code that can read or act on that resource without having acquired it itself - a different user's concurrent request, a child process, another thread.
- Data Flow / Missing Controls: Look for missing explicit scoping (no per-request instance, no context/block scope), missing cleanup (a handle or connection that is never released), and missing permission or close-on-exec restriction when a resource crosses a process boundary.
Scope Resources Explicitly (Primary Defense)
- Use local variables, per-request objects, or explicit scoping constructs (block scope, context managers, request-scoped objects) instead of globals or statics for anything tied to a single user, request, or transaction.
- Acquire the resource as late as possible and release it as soon as the scope that needs it ends.
- For state shared across concurrent requests or threads, use thread-local or request-scoped storage so each execution context gets its own copy instead of a shared one.
Isolate Resources Across Process and Thread Boundaries
- Mark file descriptors and sockets close-on-exec at the point they are opened, so the kernel drops them when the child
execs. The flag acts atexecand not atfork: a forked child that runs lower-trust code without exec'ing still holds a copy of the whole descriptor table, so close those handles explicitly in the child before the work starts. - Restrict permissions on shared memory, IPC objects, and temporary files to the minimum set of principals that need them - never default to world-readable or world-writable.
- Use a connection or resource per request or transaction rather than a single connection reused across users.
Add Resource Cleanup (Defense in Depth)
- Ensure every acquired resource has a matching release on every exit path, including exceptions - return connections to the pool, close files, clear thread-local state.
- Run static analysis or resource-leak detection to catch resources that are acquired but never released.
Test Cross-Boundary Access
- Verify User A's request cannot read or influence User B's concurrently running request.
- Verify a spawned child process cannot access file descriptors, memory, or sockets the parent did not intend to share.
- Re-scan to confirm the finding is resolved.
Common Vulnerable Patterns
Per-request state held in a shared scope
// VULNERABLE - resource in global/static scope, shared across requests
global current_user
on_request(req):
current_user = authenticate(req)
// A concurrent request can overwrite current_user before this one
// finishes, so this request may end up acting as a different user
do_something_with(current_user)
Why this is vulnerable: the identity of the caller is per-request data stored somewhere that outlives the request and is shared by all of them. MITRE files this shape as CWE-488 (Exposure of Data Element to Wrong Session), and its own example is a servlet member field, which is the form this most often takes in practice. Two requests in flight at once write to the same variable, and the second write lands before the first request has finished reading, so a request authenticated as one user carries out its work as another. Nothing fails, nothing is logged as an error, and the resulting action is fully authorised - for the wrong person.
Two properties make this hard to find. It requires concurrency, so it is absent from every single-request test and from local development, and appears under load in proportion to traffic. The window is also small, so the symptom is rare and intermittent. It gets reported as data appearing under the wrong account rather than as a security defect, and tends to be investigated as a caching bug. The fix is scope rather than locking: carry the identity in the request context or pass it as an argument, so there is no shared slot to race for.
A handle inherited across a process boundary
// VULNERABLE - handle inherited across a process boundary
fd = open("/etc/secrets", READ_ONLY)
pid = fork()
if pid == 0: // fork() returns 0 in the child
exec("/bin/untrusted_program")
// The child inherits fd and can read the parent's file
Why this is vulnerable: the access check happened when the parent opened the file, and the handle that resulted carries that decision with it. Handing it to a child process transfers the permission without re-checking anything, so the child reads a file it could not have opened itself. Whether it happens without anyone asking for it depends on the layer. At the POSIX level, inheritance across exec is the default, so C, C++ and anything calling open directly get it automatically. Python (non-inheritable since 3.4), Java's ProcessBuilder and Node's child_process all default the other way, so there the finding is usually an explicit opt-in someone added.
The consequence is that the child's own privileges stop describing what it can reach. Dropping to an unprivileged user before the exec does not close it, because the descriptor is already open and the permission was resolved earlier. Two things do close it: marking handles close-on-exec at the point they are opened, or auditing what is open immediately before spawning. The second is harder to keep correct as the code grows, since every new open elsewhere in the process becomes part of what the child inherits. CWE-402 is the child to use for this case, and carries the open-time flags and the per-runtime detail on what each spawn API hands the child.
Secure Patterns
// SECURE - resource scoped to the request, not shared
on_request(req):
with request_scope() as ctx:
ctx.user = authenticate(req)
do_something_with(ctx.user)
// ctx is created fresh per request and discarded when the request
// completes - no other request can see or overwrite it
// SECURE - handle closed before crossing the process boundary
fd = open("/etc/secrets", READ_ONLY, CLOSE_ON_EXEC)
pid = fork()
if pid == 0: // fork() returns 0 in the child
exec("/bin/untrusted_program")
// fd was marked close-on-exec, so the kernel drops it here at exec
// and the untrusted program never has it open
Why this works: Scoping a resource to the request or transaction that owns it gives each concurrent request its own copy, so one user's authentication state, buffer, or connection cannot bleed into another's. Marking handles close-on-exec means the kernel drops them at exec, so the program the child becomes has only what it was deliberately given rather than everything the parent happened to have open. Where the child does not exec at all, close the handles explicitly in the child instead, because fork copies the descriptor table whatever the flag says. Both patterns replace an implicit scope with one that is created, used, and torn down inside a single defined boundary.