Skip to content

CWE-498: Cloneable Class Containing Sensitive Information

Overview

Cloneable classes with sensitive data (passwords, keys, tokens) allow unauthorized duplication through clone() method, creating uncontrolled copies that bypass security controls, aren't tracked, and may not be properly cleared from memory.

Risk

Medium: Cloneable sensitive classes enable unauthorized data copies, bypass access controls, create untracked credential copies, prevent secure memory clearing, and allow privilege escalation through cloned security contexts.

Remediation Steps

Core principle: Do not allow cloning/copying to leak secrets; prevent cloning or scrub sensitive fields on clone/serialize.

Locate Cloneable Classes with Sensitive Data

When reviewing security scan results:

  • Examine data_paths: Identify classes that implement Cloneable and contain sensitive data
  • Find sensitive fields: Passwords, keys, tokens, PII, credentials, security contexts
  • Check clone() implementations: Look for shallow copies of sensitive data
  • Review class hierarchy: Check if sensitive classes can be subclassed
  • Assess risk: Can cloning bypass access controls or create untracked copies

Sensitive data types:

  • Authentication credentials (passwords, tokens)
  • Cryptographic keys (encryption keys, signing keys)
  • Security contexts (user roles, permissions)
  • Personal information (SSN, credit cards)
  • Session data

Make Class Final to Prevent Cloning (Primary Defense)

// Prevent cloning by making class final and not implementing Cloneable
public final class Credentials {  // final prevents subclassing
    private final String username;
    private final char[] password;
    private final byte[] encryptionKey;

    public Credentials(String username, char[] password, byte[] key) {
        this.username = username;
        this.password = Arrays.copyOf(password, password.length);
        this.encryptionKey = Arrays.copyOf(key, key.length);
    }

    // No clone method - cloning not supported
    // Class is final - can't be subclassed to add clone()

    public void clear() {
        Arrays.fill(password, '\0');
        Arrays.fill(encryptionKey, (byte) 0);
    }
}

Why this works: Making a class final prevents it from being subclassed, which prevents attackers from creating a subclass that implements cloning. Not implementing Cloneable means clone() will throw CloneNotSupportedException.

Override clone() to Prevent Cloning

If class can't be final (e.g., part of framework):

public class SecureToken {
    private String token;
    private byte[] secret;
    private long expiryTime;

    @Override
    public final Object clone() throws CloneNotSupportedException {
        // Explicitly prevent cloning of sensitive data
        throw new CloneNotSupportedException(
            "Cloning of SecureToken is not permitted for security reasons"
        );
    }

    // Alternative: provide controlled copy method
    public SecureToken createRenewal() {
        // Check permissions
        if (!hasPermission("token.renew")) {
            throw new SecurityException("Not authorized to renew token");
        }

        // Create new token (not a clone)
        return new SecureToken(generateNewToken());
    }
}

Implementation notes:

  • Make clone() final so subclasses can't override it
  • Throw CloneNotSupportedException with clear message
  • Provide alternative controlled copy methods if needed

Don't Implement Cloneable Interface

// WRONG - implements Cloneable with sensitive data
public class Password implements Cloneable {  // Don't do this!
    private char[] passwordChars;

    @Override
    public Object clone() {
        try {
            Password cloned = (Password) super.clone();
            // Even with deep copy, creates untracked copy
            cloned.passwordChars = passwordChars.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

// CORRECT - no Cloneable interface
public final class Password {  // No implements Cloneable
    private final char[] passwordChars;

    public Password(char[] password) {
        this.passwordChars = Arrays.copyOf(password, password.length);
    }

    // Provide controlled access
    public char[] getPassword() {
        return Arrays.copyOf(passwordChars, passwordChars.length);
    }

    public void clear() {
        Arrays.fill(passwordChars, '\0');
    }

    // No clone method at all
}

Implement Deep Clone with Access Control (If Cloning Required)

If cloning is genuinely needed:

public class ApiKey implements Cloneable {
    private String keyId;
    private byte[] secretKey;
    private Set<String> permissions;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // Check security permissions before allowing clone
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("cloneApiKey"));
        }

        // Additional business logic check
        if (!currentUser().hasRole("ADMIN")) {
            throw new CloneNotSupportedException("Only admins can clone API keys");
        }

        // Perform deep copy
        ApiKey cloned = (ApiKey) super.clone();
        cloned.secretKey = this.secretKey.clone();
        cloned.permissions = new HashSet<>(this.permissions);

        // Audit logging
        auditLog.log("ApiKey cloned by " + currentUser().getName(), 
                     "keyId=" + keyId, 
                     "timestamp=" + Instant.now());

        // Track cloned instances
        registerClone(cloned);

        return cloned;
    }
}

Access control measures:

  • Check SecurityManager permissions
  • Verify user authorization
  • Perform deep copy (not shallow)
  • Log all cloning operations
  • Track cloned instances

Verify Cloning is Prevented

Manual verification steps:

Check class does not implement Cloneable

Review sensitive classes to ensure they don't implement Cloneable

# Search for classes implementing Cloneable
grep -r "implements.*Cloneable" src/

# Check specific sensitive classes
grep "class Credentials\|class SecurityContext\|class User" src/ -A 1 | grep Cloneable

Verify clone() method throws exception

If clone() is inherited, ensure it throws CloneNotSupportedException

// Check the implementation:
@Override
protected final Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException("Cloning not allowed");
}

Confirm class is final

Prevent subclasses from overriding clone protection

# Verify sensitive classes are final
grep -E "class (Credentials|User|SecurityContext|Session)" src/ | grep final

Manual testing

Attempt to clone sensitive objects and verify it fails

// In your development environment, try:
Credentials creds = new Credentials("user", "pass123".toCharArray());
try {
    Credentials cloned = (Credentials) creds.clone();
    System.err.println("ERROR: Cloning succeeded when it should fail!");
} catch (CloneNotSupportedException e) {
    System.out.println("✓ Cloning correctly prevented: " + e.getMessage());
}

Automated verification

# Use static analysis to detect Cloneable implementation
# PMD rule: AvoidCloneableImplementation (for security-sensitive classes)
# SpotBugs detector: CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE

mvn pmd:check
mvn spotbugs:check

Code review checklist

  • Sensitive classes do NOT implement Cloneable
  • If clone() exists, it throws CloneNotSupportedException
  • Classes are marked final to prevent subclass bypass
  • Copy constructors are used instead of cloning
  • Defensive copying is used for mutable fields

Monitoring

  • Log any clone() calls on sensitive classes
  • Alert on CloneNotSupportedException from sensitive classes
  • Track object lifecycle for sensitive data

Common Vulnerable Patterns

Cloneable Class with Shallow Copy of Sensitive Data

// VULNERABLE - Cloneable with sensitive data
public class User implements Cloneable {
    private String username;
    private String password;  // Sensitive!
    private byte[] secretKey;  // Sensitive!

    @Override
    public Object clone() {
        try {
            return super.clone();  // Shallow copy!
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

// Attack example:
User user = authenticate();
User clonedUser = (User) user.clone();  // Unauthorized copy!
// Now clonedUser has password and secretKey
// Result: Attacker has cloned credentials bypassing access controls

Attack Scenarios

Bypass Access Control via Cloned Security Context

// Attack example: Clone privileged context for later use
SecurityContext context = getSecurityContext();
if (context.isAdmin()) {
    // Clone and use later when not admin
    SecurityContext cloned = (SecurityContext) context.clone();
    // Use cloned context to bypass checks
}

Untracked Credentials After Clearing

// Attack example: Keep copy of credentials after original is cleared
Credentials creds = login();
Credentials backup = (Credentials) creds.clone();
// Original cleared but backup still has password
creds.clear();
// backup still contains the password in memory

Secure Patterns

Use Final Class with No Cloning Support

// SECURE - Immutable final class with no cloning
public final class SecureCredentials {
    private final String username;
    private final char[] password;

    public SecureCredentials(String user, char[] pass) {
        this.username = user;
        this.password = Arrays.copyOf(pass, pass.length);
    }

    // Factory method instead of clone
    public static SecureCredentials create(String user, char[] pass) {
        return new SecureCredentials(user, pass);
    }

    public void clear() {
        Arrays.fill(password, '\0');
    }
}

Why this works: Declaring the class as final prevents subclassing, which means attackers cannot create a derived class that implements Cloneable or overrides clone() to create unauthorized copies. By not implementing the Cloneable interface, any attempt to call clone() will throw CloneNotSupportedException. The defensive copy in the constructor (Arrays.copyOf()) ensures that the original password array passed in cannot be modified externally. Instead of cloning, the create() factory method provides a controlled way to create new instances while maintaining proper security controls. The clear() method allows secure disposal of sensitive data by overwriting the password array with zeros, ensuring only one tracked instance needs to be cleared rather than worrying about untracked clones.

Security Checklist

  • Sensitive classes are final
  • Sensitive classes don't implement Cloneable
  • If clone() exists, it throws CloneNotSupportedException
  • clone() method is final (can't be overridden)
  • No shallow copies of sensitive data
  • Cloning is logged/audited if allowed
  • Access controls checked before cloning

Additional Resources