CWE-498 describes a class that holds sensitive data but can be cloned. Anyone with a reference to an instance can make a copy and read the data out of it. It applies to object-oriented code in Java, C++ and C#.
How it happens
A cloneable class is effectively an open class. Data inside it cannot be hidden, because any other class can clone it without running the constructor. If the class does not explicitly deny cloning, the default behaviour lets that happen.
The constructor is where many classes do their security work: checking who is calling, validating arguments, loading a key. A clone skips all of that.
// Vulnerable: Cloneable, holds a secret, constructor enforces a check
public class ApiCredential implements Cloneable {
private String token;
public ApiCredential(String token, User caller) {
if (!caller.isAdmin()) throw new SecurityException("denied");
this.token = token;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // copy made, admin check never ran
}
}
What an attacker gains
MITRE lists the consequence as an access control bypass. The copy exists without the constructor's checks having run, so the protection that guarded creation of the object no longer guards its data.
How to find it
Static analysis can find some instances by looking for classes that implement cloning and hold sensitive fields. In review, search for Cloneable, clone() and MemberwiseClone and ask what each class stores.
How to fix it
Do not make the class cloneable unless you need to. If you must, make the clone method final so a subclass cannot loosen it, and make it refuse the copy.
public final class ApiCredential {
private String token;
@Override
public final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
For sensitive data, a copy constructor that repeats the checks is safer than cloning.
Quick check
Why is cloning dangerous when a constructor does security checks?
Cloning creates an object without running the constructor, so those checks are skipped.