Cloneable Class Containing Sensitive Information
Description
Cloneable Class Containing Sensitive Information is a vulnerability where a class that holds or processes sensitive data implements the Cloneable interface or otherwise permits cloning. Cloneable classes are effectively open classes since data cannot be hidden in them - any code can create copies of objects containing sensitive information. The cloning operation creates a new object without invoking the constructor, potentially bypassing security checks, initialization logic, or access controls that would normally protect the sensitive data during object creation.
Risk
Classes containing sensitive data like credentials, cryptographic keys, financial information, or personal data become vulnerable when cloneable. Attackers can clone sensitive objects to create unauthorized copies, bypass constructor-based security validations, or extract protected information. The cloned object has the same data as the original but may exist outside the intended security context. Clone operations may also leak sensitive data if the clone method is overridden maliciously or if shallow cloning exposes internal references. In serialization-based attacks, cloning can be combined with deserialization to extract or manipulate sensitive data.
Solution
Never implement Cloneable on classes containing sensitive information. If a class must be cloneable for other reasons, override the clone method as final and throw CloneNotSupportedException to explicitly prevent cloning. Use copy constructors or factory methods with proper access controls as alternatives when controlled copying is needed. For existing classes that are cloneable, ensure sensitive fields are marked as transient or implement a custom clone method that properly protects or clears sensitive data. Review third-party libraries for classes that might expose sensitive data through cloning.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - Sensitive data can be extracted by cloning objects, allowing unauthorized access to credentials, keys, or personal information. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Objects can be cloned without executing the constructor, bypassing security checks that would normally occur during object instantiation. |
Example Code
Vulnerable Code
// Vulnerable: Cloneable class with sensitive data
public class VulnerableUserCredentials implements Cloneable {
private String username;
private String password;
private String sessionToken;
private byte[] privateKey;
public VulnerableUserCredentials(String username, String password) {
// Constructor performs security validation
if (!validateCredentials(username, password)) {
throw new SecurityException("Invalid credentials");
}
this.username = username;
this.password = hashPassword(password);
this.sessionToken = generateSecureToken();
this.privateKey = loadPrivateKey(username);
}
// Vulnerable: Default clone allows credential copying
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // Copies all fields including sensitive data
}
private boolean validateCredentials(String user, String pass) {
// Security validation bypassed on clone
return user != null && pass != null && pass.length() >= 8;
}
private String hashPassword(String password) {
return BCrypt.hashpw(password, BCrypt.gensalt());
}
private String generateSecureToken() {
return UUID.randomUUID().toString();
}
private byte[] loadPrivateKey(String username) {
// Load user's private key
return KeyStore.getPrivateKey(username);
}
// Getters...
}
// Attacker exploits cloning
public class CredentialExploit {
public void exploit(VulnerableUserCredentials validCreds)
throws CloneNotSupportedException {
// Clone the credentials object - bypasses validation
VulnerableUserCredentials clonedCreds =
(VulnerableUserCredentials) validCreds.clone();
// Attacker now has:
// - Copy of session token
// - Copy of private key
// - All credential data without authentication
}
}
// Vulnerable: Teacher/Student records with sensitive data
public class VulnerableStudentRecord implements Cloneable {
private String name;
private String studentId;
private String socialSecurityNumber;
private String address;
private double gpa;
private List<String> medicalConditions;
// Constructor with access control
public VulnerableStudentRecord(String name, String id,
AdminContext admin) {
if (!admin.hasPermission("CREATE_STUDENT_RECORD")) {
throw new SecurityException("Unauthorized");
}
this.name = name;
this.studentId = id;
// ... initialize other fields
}
// Vulnerable: Anyone can clone sensitive student data
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// Vulnerable: Financial transaction
public class VulnerableTransaction implements Cloneable {
private String transactionId;
private String accountNumber;
private double amount;
private String authorizationCode;
private byte[] signature;
// Vulnerable: Clone exposes financial data
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// Vulnerable: Cloneable through inheritance
public class VulnerableSensitiveData extends CloneableBase {
// Inherits clone() from parent
private String apiKey;
private String secretToken;
// Even without explicit Cloneable, parent's clone works
}
// Vulnerable: Cloneable enum-like pattern
public class VulnerablePermission implements Cloneable {
public static final VulnerablePermission ADMIN =
new VulnerablePermission("ADMIN", 100);
public static final VulnerablePermission USER =
new VulnerablePermission("USER", 10);
private String name;
private int level;
private VulnerablePermission(String name, int level) {
this.name = name;
this.level = level;
}
// Vulnerable: Allows cloning "singleton" permissions
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Fixed Code
// Fixed: Class explicitly prevents cloning
public final class SecureUserCredentials {
private final String username;
private final String passwordHash;
private final String sessionToken;
private final byte[] privateKey;
public SecureUserCredentials(String username, String password) {
if (!validateCredentials(username, password)) {
throw new SecurityException("Invalid credentials");
}
this.username = username;
this.passwordHash = hashPassword(password);
this.sessionToken = generateSecureToken();
this.privateKey = loadPrivateKey(username);
}
// Fixed: Explicitly prevent cloning
@Override
public final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException(
"Cloning of credentials is not permitted"
);
}
// Fixed: No Cloneable interface implemented
// Fixed: Class is final - cannot be subclassed to add Cloneable
private boolean validateCredentials(String user, String pass) {
return user != null && pass != null && pass.length() >= 8;
}
private String hashPassword(String password) {
return BCrypt.hashpw(password, BCrypt.gensalt());
}
private String generateSecureToken() {
return UUID.randomUUID().toString();
}
private byte[] loadPrivateKey(String username) {
return KeyStore.getPrivateKey(username);
}
// Fixed: Controlled copy with security checks
public static SecureUserCredentials createAuthorizedCopy(
SecureUserCredentials original,
SecurityContext context) {
if (!context.canCopyCredentials(original.username)) {
throw new SecurityException("Not authorized to copy credentials");
}
// Create new instance through constructor with proper checks
// Note: This requires re-authentication, not just copying
return new SecureUserCredentials(
original.username,
context.getAuthenticatedPassword()
);
}
}
// Fixed: Student record that cannot be cloned
public final class SecureStudentRecord {
private final String name;
private final String studentId;
private final String socialSecurityNumber; // Encrypted
private final String address;
private final double gpa;
private final List<String> medicalConditions;
private SecureStudentRecord(Builder builder, AdminContext admin) {
if (!admin.hasPermission("CREATE_STUDENT_RECORD")) {
throw new SecurityException("Unauthorized");
}
this.name = builder.name;
this.studentId = builder.studentId;
this.socialSecurityNumber = encrypt(builder.ssn);
this.address = builder.address;
this.gpa = builder.gpa;
this.medicalConditions = Collections.unmodifiableList(
new ArrayList<>(builder.medicalConditions)
);
}
// Fixed: No clone method - class is not Cloneable
// Fixed: Final class prevents subclassing
// Fixed: Authorized access to sensitive data
public String getSSN(AdminContext admin) {
if (!admin.hasPermission("VIEW_SSN")) {
throw new SecurityException("Unauthorized to view SSN");
}
auditLog("SSN accessed for: " + studentId);
return decrypt(socialSecurityNumber);
}
// Fixed: Controlled export with redaction
public StudentRecordDTO toDTO(AdminContext admin) {
StudentRecordDTO dto = new StudentRecordDTO();
dto.name = this.name;
dto.studentId = this.studentId;
dto.gpa = admin.hasPermission("VIEW_GPA") ? this.gpa : null;
// SSN never exported in DTO
return dto;
}
private String encrypt(String data) {
return EncryptionService.encrypt(data);
}
private String decrypt(String data) {
return EncryptionService.decrypt(data);
}
// Builder pattern for controlled construction
public static class Builder {
// ... builder fields and methods
}
}
// Fixed: Transaction that protects sensitive data
public final class SecureTransaction {
private final String transactionId;
private final String maskedAccountNumber;
private final double amount;
private final byte[] encryptedAuthCode;
public SecureTransaction(TransactionRequest request,
SecurityContext context) {
if (!context.isAuthorized()) {
throw new SecurityException("Unauthorized transaction");
}
this.transactionId = generateTransactionId();
this.maskedAccountNumber = maskAccount(request.getAccountNumber());
this.amount = request.getAmount();
this.encryptedAuthCode = encryptAuthCode(
generateAuthCode(),
context.getEncryptionKey()
);
}
// Fixed: Explicitly throw on clone attempt
@Override
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException(
"Financial transactions cannot be cloned"
);
}
private String maskAccount(String account) {
return "****" + account.substring(account.length() - 4);
}
private String generateAuthCode() {
return SecureRandom.generateAuthorizationCode();
}
private byte[] encryptAuthCode(String code, Key key) {
return Cipher.encrypt(code.getBytes(), key);
}
}
// Fixed: Immutable permission enum (no cloning possible)
public enum SecurePermission {
ADMIN(100),
MODERATOR(50),
USER(10),
GUEST(1);
private final int level;
SecurePermission(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
// Enums cannot be cloned - clone() throws CloneNotSupportedException
}
CVE Examples
No specific CVEs are listed in the MITRE database for this CWE. However, the vulnerability pattern is documented in:
- CERT Oracle Secure Coding Standard for Java (OBJ07-J)
- Java security best practices
References
- MITRE Corporation. "CWE-498: Cloneable Class Containing Sensitive Information." https://cwe.mitre.org/data/definitions/498.html
- CERT Oracle Secure Coding Standard for Java. "OBJ07-J. Sensitive classes must not let themselves be copied."
- Oracle. "Secure Coding Guidelines for Java SE."