This is a brainstorming question about what's possible in Java (or not). I want to know if it is possible to hide a secret within a class and prevent anymore from accessing it using Java code or any of its feature only (security, reflexion, serialization, class loaders, you-name-it...).
Here is what I have in mind so far:
public final class Safe {
private String secret;
private HashMap<String, Credentials> validCertificates
= new HashMap<String, Credentials>();
public Safe(String aSecret) {
this.secret = aSecret;
}
public final class Credentials {
private String user;
private Credentials(String user) {
this.user = user;
}
}
public final Credentials getCredential(String user) {
// Following test is just for illustrating the intention...
if ( "accepted".equals(user) ) {
return new Credentials(user);
} else {
return null;
}
}
public String gimmeTheSecret(Credentials cred) {
if ( this.validCertificates.get(cred.user) == cred ) {
return secret;
} else {
return null;
}
}
private void writeObject(ObjectOutputStream stream) throws IOException {
throw new RuntimeException("No no no no no no no!!!");
}
}
Can it be improved? Should it be improved? Is the idea of locking a secret in a safe class impossible to achieve?
EDIT
Relevance:
Some people question the relevance of the issue I am raising here. Although I am asking a general question in order to trigger an open conversation, there is a very concrete application to this class:
Clarification:
Facts:
Solved issues:
Regarding the execution environment, we need to distinguish two cases:
If we set a proper SecurityManager disabling reflection and restricting permissions on any loaded untrusted code, then our secret is safe.
The hacker can create his own application with its own security manager and Secure Class loader. It could load our code from the classpath and execute it as if it were our own application. In this case, he could break the safe.
No, it's not safe from other Java code. Your secret could be retrieved from an instance of Safe
like this:
Field field = safe.getClass().getDeclaredField("secret");
field.setAccessible(true);
String secret = (String) field.get(safe);
Update: If you control the loading of the other Java code that you want to hide the secret from you can probably use a custom SecurityManager
or ClassLoader
to prevent access to it. You need to control the environment that this runs in to work though, e.g. a server you restrict access to.
Your edited question however mentions that the code can run on any desktop or device. In that case there's really nothing you can do to protect the secret from other processes that could do just about anything. Even if you encrypt it in memory another process can just intercept the key or even the plaintext secret as its passed around.
If you don't control the environment that you need something to be secure in then you likely need to consider a different approach. Perhaps you can avoid storing the secret in memory altogether?
This "security" is laughable.
Where does it run? On my desktop? I connect to the JVM with debugger and view all the secrets in clear text.
Or I place my code next to it and use reflection to dump the content.
Or I inject my own code modification via BCEL, and modify the constructor of Safe to dump the "secret" value to a file.
Or I simply replace the whole package with mine with the same name by placing it into bootstrap classloader.
Or I can even modify and compile java sources to get a modified JVM.
Or... my, one can list dozens of ways to extract a value from a runtime instance!
The real question in any security design is: who is a attacker? What is the threat model? Without answering this the topic is pointless.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With