Returning a Mutable Object to an Untrusted Caller
Description
Returning a Mutable Object to an Untrusted Caller is a vulnerability that occurs when functions return references to mutable internal data without cloning. When non-cloned mutable data is returned, external calling code can modify it, potentially violating the class's internal state assumptions and breaking encapsulation. This weakness is the counterpart to CWE-374 (Passing Mutable Objects to an Untrusted Method) - together they form the two directions of mutable object exposure.
Risk
Returning mutable objects to untrusted callers allows those callers to tamper with internal data that they shouldn't have access to modify. This violates access control and data integrity principles. Security-relevant data like permission sets, authentication states, or configuration may be altered by code that receives the mutable reference. Internal invariants that the class depends on can be broken, causing subsequent operations to behave incorrectly or fail. The modification may not be detected until much later, making debugging difficult.
Solution
Clone all mutable data before returning references to external code. Declare returned data as constant or immutable where appropriate. Use Collections.unmodifiableList() or similar methods in Java. Return defensive copies that the caller can modify freely without affecting internal state. Consider using immutable data structures from the start. For arrays, return a new array copy rather than the original. Document clearly when returned objects are mutable copies versus views.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Data can be tampered with by functions that shouldn't have access to modify it, violating access control and integrity principles. |
Example Code
Vulnerable Code
// Vulnerable: Returning mutable internal object
public class VulnerableClinicalTrial {
private List<Patient> patients = new ArrayList<>();
private Date startDate;
private Map<String, String> config = new HashMap<>();
public List<Patient> getPatients() {
// Vulnerable: Returns direct reference to internal list
return patients;
// Caller can add/remove patients!
}
public Date getStartDate() {
// Vulnerable: Date is mutable
return startDate;
// Caller can modify: getStartDate().setTime(0);
}
public Map<String, String> getConfig() {
// Vulnerable: Returns internal map
return config;
}
}
// Attacker code
VulnerableClinicalTrial trial = getTrial();
trial.getPatients().clear(); // Deletes all patients!
trial.getStartDate().setTime(0); // Corrupts date
trial.getConfig().put("encryption", "none"); // Disables security
# Vulnerable: Returning mutable internal state
class VulnerableUserManager:
def __init__(self):
self._users = []
self._permissions = {}
def get_users(self):
# Vulnerable: Returns internal list
return self._users
def get_permissions(self):
# Vulnerable: Returns internal dict
return self._permissions
# Attacker code
manager = VulnerableUserManager()
manager.get_users().append(malicious_admin) # Add unauthorized user
manager.get_permissions()["admin"] = True # Escalate privileges
// Vulnerable: Returning pointer to internal buffer
typedef struct {
char password[64];
int privilege_level;
} UserSession;
static UserSession current_session;
// Vulnerable: Returns pointer to internal data
UserSession* get_current_session() {
return ¤t_session;
}
// Attacker code
UserSession *session = get_current_session();
session->privilege_level = ADMIN_LEVEL; // Privilege escalation
Fixed Code
// Fixed: Clone mutable objects before returning
public class SecureClinicalTrial {
private List<Patient> patients = new ArrayList<>();
private Date startDate;
private Map<String, String> config = new HashMap<>();
public List<Patient> getPatients() {
// Fixed: Return defensive copy
List<Patient> copy = new ArrayList<>();
for (Patient p : patients) {
copy.add(p.clone()); // Deep copy if Patient is mutable
}
return copy;
// Or return unmodifiable view:
// return Collections.unmodifiableList(patients);
}
public Date getStartDate() {
// Fixed: Return clone of mutable Date
return (Date) startDate.clone();
}
public Map<String, String> getConfig() {
// Fixed: Return unmodifiable view
return Collections.unmodifiableMap(config);
}
}
// Better: Use immutable types
public class ImmutableClinicalTrial {
private final List<Patient> patients;
private final Instant startDate; // Instant is immutable
private final Map<String, String> config;
public ImmutableClinicalTrial(List<Patient> patients,
Instant startDate,
Map<String, String> config) {
this.patients = List.copyOf(patients); // Immutable copy
this.startDate = startDate;
this.config = Map.copyOf(config); // Immutable copy
}
public List<Patient> getPatients() {
return patients; // Already immutable
}
public Instant getStartDate() {
return startDate; // Immutable
}
}
# Fixed: Return copies of mutable objects
import copy
class SecureUserManager:
def __init__(self):
self._users = []
self._permissions = {}
def get_users(self):
# Fixed: Return deep copy
return copy.deepcopy(self._users)
def get_permissions(self):
# Fixed: Return copy
return self._permissions.copy()
# Fixed: Return immutable view using tuple/frozenset
def get_user_ids(self):
return tuple(user.id for user in self._users)
# Using dataclasses with frozen=True for immutability
from dataclasses import dataclass
from typing import Tuple
@dataclass(frozen=True)
class ImmutableUserData:
users: Tuple[str, ...] # Immutable tuple
permissions: frozenset # Immutable set
// Fixed: Return copy of data
typedef struct {
char password[64];
int privilege_level;
} UserSession;
static UserSession current_session;
// Fixed: Return copy, not pointer
int get_current_session(UserSession *copy) {
if (copy == NULL) {
return -1;
}
// Fixed: Copy data to caller-provided buffer
memcpy(copy, ¤t_session, sizeof(UserSession));
return 0;
}
// Fixed: Return const pointer for read-only access
const UserSession* get_session_readonly() {
return ¤t_session;
// Compiler enforces read-only
}
CVE Examples
No specific CVEs are listed for this CWE. The vulnerability pattern appears in:
- Java applications exposing internal collections
- APIs returning Date objects or arrays
- Object-oriented code with inadequate encapsulation
References
- MITRE Corporation. "CWE-375: Returning a Mutable Object to an Untrusted Caller." https://cwe.mitre.org/data/definitions/375.html
- Joshua Bloch. "Effective Java." Item 50: Make defensive copies when needed.