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:
- If I want to decrypt some messages, I need to load a private key data into a class. If I can't prevent other Java code from accessing it, then it is impossible to create a secure system. Of course, if I want to decrypt a message, I should rather do it in the class than giving away the secret, but still, the safe has to remain unbreakable.
Clarification:
- Instances of the class are only created at runtime, not at compile time
- Code can run in web server applications or any desktop or device applications
- The class is only used to store a secret at runtime, in memory, no plans to persist it (for persistence, one can/should use classic encryption techniques)
Facts:
- To implement security in a Java application, one should set a SecurityManager instance where checking methods are overridden as needed
- This application can load untrusted code with secure class loaders and assign a protection domain for the classes it loads. This domain should not include a RuntimePermission("setSecurityManager").
- Untrusted code can try to change the SecurityManager, but since the Secure Class Loader did not grant the setSecurityManager permission, a SecurityException will be thrown.
Solved issues:
Regarding the execution environment, we need to distinguish two cases:
- Controlled environment: We get to start the application that will use untrusted code trying to break our 'safe'.
If we set a proper SecurityManager disabling reflection and restricting permissions on any loaded untrusted code, then our secret is safe.
- Uncontrolled environment: Hacker gets to start the application which uses untrusted code trying to break our '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.
- As established in a separate question, sun.misc.Unsafe cannot break a security manager
There is another angle to this issue: Java delivered Permissions. In a context where the environment is controlled, one can assign a set of permissions to classes loaded with a secure class loader.
Java delivers 3 permissions objects related to the implementation of security:
These can help improve and control access to functionalities when implementing a cryptography system. They are necessarily sufficient to make the system secure.
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 need to run untrusted code, the best way to do this is to run it in a separate JVM. This way the untrusted code can be given the strictest limitations and even be killed if for example you have a run away CPU or crash the JVM. The only access the code can make is via the means you provide it an use reflection will not give you access to classes in another JVM no matter what you do.
You can even construct a jail or maze for your application. This can allow the untrusted code to run in what appears to be a real system, but in reality it is not. (The purpose of which is to tie you any would be hacker long enough to see what they are doing)
You could even run the JVM in its own virtual machine so it looks like you have complete (but dummy) system to "invade". A virtual machine can be saved for analysis and wiped to a preset state very easily.
The ultimate solution is to place the untrusted code on a dummy LAN of its own. (Something they did to analyse the stuxnet worm. ;)
No, you cannot embed a secret in a class that way. If you compiled that, then I could just run
strings
on the resulting class file and it might be in there.One way you may go about doing this if you're only trying to validate it is by storing a hash in it. Once you want to validate the input, hash that and compare the hashes.
First in security, unbreakable doesn't exist. There is tiny chance that by random I find your encription key by just writting random things on my keyboard. If your key is complex is would make you a really unlucky man, but this is possible.
Second point, using a public/private key value pair to protect communications between a client and a server is really common and it works perfectly if done right. But one must understand that this really protect the communication between the two computers. It doesn't protect the computer themselves. The whole thing is based on total trust the computer have from each-other.
Third point, when you have physical access to a computer you can do everything you want with it, in particular this include spying everything a program do. And all content of the computer. Content can be encrypted yes, but while being used, it is not encrypted anymore. This is a major problem of public/private key system : the private key are stored somewhere so you must be sure this place is safe.
This flow can be perfectly acceptable for you if you trust the computers involved in the communication. This is the case for exemple when you connect to your bank account.
Bank computers are trusted by the bank, and the provided access to external word is really restricted and controlled. They are "safe".
If you give away your own private key or access credential to the bank. You are compromized, but it is your responsability and problem. Because it is not in your interrest to be compromised, you'll do your best to avoid that. That nice, because you are the one with full control on your computer.
But let say you access to your bank from a public computer, or a computer from another person. Then a simple keylogger can just record the keys and mouse move you make when you enter your password.
Security on client side is based on trust you can have on the client. If you can trust him, this is perfect, it work. If you can't, then it is broken.
You can make a secret "hard" to access but you can't make it impossible. There's a saying (Bruce Schneier I believe): Against a casual user, anything works. Against a determined cracker nothing works.