8

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 anyone from accessing it using Java code or any of its feature only (security, reflection, 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.

Jens Bannmann
  • 4,845
  • 5
  • 49
  • 76
Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453
  • No, there's not. Security through obscurity fails in pretty much all cases. – Brian Roach Apr 23 '11 at 01:33
  • @Brian Roach Ok, but in that case, how do you break the class described above? – Jérôme Verstrynge Apr 23 '11 at 01:36
  • Can you clarify what specifically you're trying to protect against? Where is this code running and what potential threats are there? E.g. is this in a deployed server environment and you're worried about other code getting access to a secret, or is it running on a client device like a phone or applet. – WhiteFang34 Apr 23 '11 at 01:45
  • @WhiteFang34 I have made another edit. The objective is to make sure other code cannot get access to the secret. – Jérôme Verstrynge Apr 23 '11 at 01:55
  • 1
    If you make a hash of the secret and store that, then no one can get it, but you can verify that a client class has the secret by comparing a hash of their supplied "secret" to the hash. http://en.wikipedia.org/wiki/Cryptographic_hash_function – Dan Rosenstark Apr 23 '11 at 02:00
  • @Yar ok, but the class must restore the plain secret, not only store a hash – Jérôme Verstrynge Apr 23 '11 at 02:03
  • Yeah hashes are kind of aggressive that way. It's hard to get your data back. – Dan Rosenstark Apr 23 '11 at 02:10
  • I enjoyed the "instances are not made at compile-time" part, BTW. – Vladimir Dyuzhev Apr 23 '11 at 02:19
  • I am adding a bounty to raise attention. If anyone has more angles to share about this issue, let us know. Thanks. – Jérôme Verstrynge Apr 25 '11 at 10:09
  • The question is more what do you want to achieve with that secret ? Why do you need at all to store it on the client ? What is the business need ? Responding to that question would allow us to better anderstand the problem and what are the best pratices in that case. – Nicolas Bousquet Apr 26 '11 at 16:11
  • The question is a general question about Java safety and security. I want to find out what is possible when it comes to holding a secret (factors, conditions, etc...). It is an exploration question. – Jérôme Verstrynge Apr 26 '11 at 17:23
  • 1
    This question doesn't have a general answer, just like "what lock is best for my door?". Common, what neighborhood are you living in? Some require no locks at all, in some you better have a metal door with bars across. Same here: the question is MEANINGLESS until you decide who's your attacker. – Vladimir Dyuzhev Apr 26 '11 at 18:59
  • You have your general question. Here a general response: You can't store AND use a secret in an unstrusted environment. There are no bargain for it. We already given explanation why. Even on your beloved server, you have to trust your administrator you have to trust the gatekeeper... This is not related to JAVA. Did you ever wondered why all software you can install endup being hacked ? Do you really think you can do better than Sony or Microsoft ? If a solution was available and well know why in hell would not everybody use it instead of being hacked? – Nicolas Bousquet Apr 27 '11 at 12:10

12 Answers12

12

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?

WhiteFang34
  • 70,765
  • 18
  • 106
  • 111
  • OK, so would encrypting the secret make any difference? – Jérôme Verstrynge Apr 23 '11 at 01:38
  • Encrypting the string might work if the key is unavailable to the other code. It might be able to find it elsewhere though, e.g. the file system. – WhiteFang34 Apr 23 '11 at 01:40
  • Couldn't the class be protected with a Secured class loader preventing reflexion? – Jérôme Verstrynge Apr 23 '11 at 01:42
  • 1
    This is true, but not true to the notion of "safety" as it means to object orientation. If you are able to use reflection, you could just as easy use `sun.misc.unsafe` or a C library to scan RAM. Nothing is every truly safe. – corsiKa Apr 23 '11 at 01:44
  • @glowcoder Can you clarify what you mean by: "safety" as it means to object orientation? Is this just a question of semantics or do you see another breach? – Jérôme Verstrynge Apr 23 '11 at 01:50
  • @glowcoder sun.misc.unsafe is a feature of Java, the rest is not (so it is not part of the question). Do you know if one can break the secure class loader with sun.misc.unsafe? – Jérôme Verstrynge Apr 23 '11 at 01:59
  • What I mean is, object orientation (and the Java language) provides mechanisms for information hiding. The typical definition of safety as it pertains to object orientation is that you are employing available oo-mechanisms to hide your information. In that sense, you are as safe as you can get. So you could consider it a matter of semantics, but I would consider it to be fundamental to the nature of oop. – corsiKa Apr 23 '11 at 01:59
  • @JVestry No, I don't know offhand what `unsafe` can break. – corsiKa Apr 23 '11 at 02:00
  • @glowcoder I get what you mean. I understand the definition you are referring to. It is not what I mean by 'safe', it is more like I am trying to lock a secret (if possible). – Jérôme Verstrynge Apr 23 '11 at 02:10
  • @WhiteFang34 I saw your edit and after doing more reading, I am coming to the same conclusion as you. I will put a bounty to raise more attention to this question and see if someone comes with a new angle. – Jérôme Verstrynge Apr 23 '11 at 13:47
12

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.

Vladimir Dyuzhev
  • 18,130
  • 10
  • 48
  • 62
  • For this case, you cannot use a debugger (c.f. question). A secure class loader will prevent you from using reflexion. About BCEL, the secret is passed at runtime in constructor, I do not see how BCEL would help here. If you still see a breach within these constraints/facts, please share. Thanks. – Jérôme Verstrynge Apr 23 '11 at 02:28
  • 8
    I control JVM. I can set any security policy I want. I can pre-load any classes I want, including my own implementation of SecureClassLoader. I'm a God and a Tsar on my JVM. – Vladimir Dyuzhev Apr 23 '11 at 02:37
  • 3
    roadto yamburg is right. Either you trust the client, and you don't have to worry about your secret to be stolen, or either you don't trust him and then he can do anything to hack it. If you don't trust the client, you should keep the secret on your side or on some trustful tier side. – Nicolas Bousquet Apr 26 '11 at 16:06
  • Nice list, and spot-on observation that if the execution plattform is compromised, there is nothing a program can do. To complement your list: One could also cause the JVM to dump its heap by sending the appropriate OS signal, one could inspect the memory of the JVM process, either in RAM or the swap file. If all else fails, one could simply pull the power cord, move the RAM chips to another computer, boot it with a specialized operating system, and read the contents of the RAM that way. (RAM maintains its state for a couple minutes after being powered off). – meriton Apr 30 '11 at 01:08
  • 1
    @road to yamburg, You forgot to mention JNI, which takes no notice of security/security managers. @meriton, I am pretty sure DRAM only keeps its state for a few second. – Peter Lawrey May 01 '11 at 09:49
  • 2
    @Peter Lawrey: Seconds to minutes at operating temperature, dozens of minutes if cooled, according to research by Ed Felten et al.: http://freedom-to-tinker.com/blog/felten/new-research-result-cold-boot-attacks-disk-encryption – meriton May 07 '11 at 16:06
4

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.

seand
  • 5,168
  • 1
  • 24
  • 37
  • @seand Yes, yes, I know I could open my PC and use some tool to scan the RAM, but the question is limited to the use of Java code and its features to achieve this. – Jérôme Verstrynge Apr 23 '11 at 02:01
  • @JVerstry understood; but just bear in mind that you're just burying it under leaves; it's not tight security. This is done all the time in the real world though. (ex. dvd 'security') – seand Apr 23 '11 at 02:03
  • 1
    This is not security through obscurity. Security through obscurity is when protection hinges on the algorithm being secret. This does not. This depends on the JVM preserving its information hiding abstractions against serialization, reflection and the like. – Mike Samuel Apr 23 '11 at 02:21
  • @Mike "Security though obscurity" may not be the precisely correct term here but we're relying on JVM restricting access. And that's not going to prevent a cracker from getting in though other means. – seand Apr 23 '11 at 02:24
  • @seand, please don't throw around vague utterances like "security through obscurity." Vague unfounded fears can lead people to adopt less secure alternatives. – Mike Samuel Apr 23 '11 at 02:27
  • Mike is correct, this is not "via obscurity". Just inadequate choice of security measures. – Vladimir Dyuzhev Apr 23 '11 at 02:45
2

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. ;)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

http://code.google.com/p/joe-e/ is an object-capability subset of java which is meant to allow decomposable security -- the ability for one part of a program to preserve its security properties even when other parts of the program are controlled, compromised, or manipulated by an attacker.

That said, valid JVMs are allowed to extend the semantics of the language with additional language facilities, such as the ability to attach a debugger at runtime. Code that can use Runtime to invoke shell access could attach a debugger to many stock JVMs and work around private access limitations even if the JVM is set up so that normal reflection respects field privateness.

Joe-E disallows a lot of the reflective abuses of information hiding that could complicate this, and of course disallows unfiltered access to Runtime.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
  • Interesting. But, you would need more than just Java code and Java features to access the secret (i.e. a debugger), correct? Moreover, calling exec can be disabled by the secure class loader. – Jérôme Verstrynge Apr 23 '11 at 02:40
  • Right. So a hacker have to run this Joe-E to prevent himself against breaking into this code... hmm... something doesn't right in this picture. May be it's the fact that a hacker would more likely run a specially modified version of JVM that _helps_ him to break the code instead? – Vladimir Dyuzhev Apr 23 '11 at 02:42
  • @road to yamburg, no, a team develops using Joe-E can be confident that the weakest developer among them can't compromise the security properties of everyone's code. Think about an operating system. The kernel developers use processor modes so that the security of the OS cannot be broken by every device driver and application author. Joe-E provides the same for user-ring code, but instead of processor modes, object boundaries are the barrier. – Mike Samuel Apr 23 '11 at 16:16
1

I think you can do this, but you end up pushing the security problem somewhere else. Is there any reason that "secret" cannot be encrypted using (for simplicity) your favourite symmetric key algorithm? The gimmeTheSecret() method would have to take an additional parameter being the secret key use to decrypt the secret.

Of course then the problem becomes that this secret key needs to be known and entered by a user or a machine storing it somewhere securely. You could use some kind of Hardware Security Module depending on how sensitive the data is and how much you want to spend!

alpian
  • 4,668
  • 1
  • 18
  • 19
  • I could indeed request a certificate from the user when he/she requests credentials and apply the traditional cryptographic methods to return the secret. I am not sure one would ever agree to give me his/her private key, because I could copy it, but I could give him/her a copy of the encrypted secret. – Jérôme Verstrynge Apr 27 '11 at 18:35
  • If I proceed so, it means that I need an encryption key to start with (for my constructor) or one should provide the encrypted secret to my constructor. Very interesting angle. Even if one fiddles with the code, they would only access the encrypted secret. Thanks! – Jérôme Verstrynge Apr 27 '11 at 18:38
  • Of course, if the hacker has control on the context of the code encrypting the secret, we're toasted, but still... – Jérôme Verstrynge Apr 27 '11 at 18:41
  • Yes this presumes that the secret is encrypted to start with and is loaded in its encrypted form. – alpian Apr 27 '11 at 18:54
1

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.

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.

Nicolas Bousquet
  • 3,990
  • 1
  • 16
  • 18
0

Adding a comment for anyone else who stumbles across this old thread....

Sometimes all you need is a 'signed' value, not an 'encrypted' value. E.g., (encoded) license tokens need to be signed but they shouldn't need to be encrypted. In these cases you can distribute the PUBLIC key and it's not an issue that anyone can see it. You're still protected since nobody else can create the signed token.

You can also use the public key to send encrypted messages to the server.

Of course this won't stop a knowledgeable and determined attacker - see the other responses. In this case someone could simply replace your public key with their own, esp. if you don't validate the cert chain. But if you can use signatures instead of encryption it will save you major headaches.

bgiles
  • 1,200
  • 11
  • 12
0

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.

icktoofay
  • 126,289
  • 21
  • 250
  • 231
  • What do you exactly mean by 'run Strings'? How does it break the class? – Jérôme Verstrynge Apr 23 '11 at 01:39
  • @JVerstry: On a UNIX box, `cd` to the directory where there's the `class` file and then do `strings - Safe.class`. It will list all ASCII character strings with a length of four or greater. – icktoofay Apr 23 '11 at 01:42
  • Ok. I have edited the post for clarification. The instances of the class are ONLY created at runtime. If so, the Strings attack falls, correct? – Jérôme Verstrynge Apr 23 '11 at 01:44
  • Strings will not work on a java class because java strings are multibyte ones, and strings is looking for ASCIIZ. Little comfort in that, though... – Vladimir Dyuzhev Apr 23 '11 at 02:35
  • @road to yamburg: That's true, but if the strings are only composed of ASCII, `strings` will find them. I tried it and it worked on the OP's source code, at least. – icktoofay Apr 23 '11 at 04:34
0

You can obfuscate it from the .class file by using proguard or similar tools. You may also sign your JAR so other packages can't access it. Signing your JAR may help too.

Vicente Plata
  • 3,370
  • 1
  • 19
  • 26
0

Assuming information passed to method calls are safe, a key is a good solution. The key doesn't need to be stored anywhere in the app, and because of this, the information can't be accessed through Java only. It gets interesting if you want a way to share the secret with others without giving them your key, which is what the shareSecret method is for below. However, it becomes tricky managing this. One process could be:

1) The secret seeker requests access, entering a temp key that is stored

2) The secret keeper grants access with their key, the temp key is deleted, and a temp Safe object is created that works for the temp key.

3) The secret seeker enters the temp key and a permanent key, the temp Safe object is deleted, and a new permanent Safe object is created that can be accessed with the permanent key.

Again, assuming parameters passed to method calls are safe, the main problem with the above procedure is that someone could have hijacked the temp key between 1 and 2 and use it to view the temp secret between steps 2 and 3. However, it would make it tougher to crack than storing it in a plain-text string.

public final class Safe {
    private String secret;

    public Safe(String secret, String key){
        this.secret = encode(secret, key}

    public String getSecret(String key){
        return decode(this.secret, credentials);
    }

    public Safe shareSecret(String fromKey, String toKey){
        return new Safe(decode(this.secret, fromKey), toKey); 
    }

    private String encode(String secret, String key){
       //Code to encode the secret based on key here...
    }

    private String decode(String secret, String key){
       //Code to decode the secret based on key here...
    }
}
Briguy37
  • 8,342
  • 3
  • 33
  • 53
  • As mentioned with @alpian, passing keys to my safe is not safe, unless these are public (or certificates). Some encryption algorithms allow Decr(A,Encr(B,Encr(A,secret))) = Encr(B,secret), which would avoid having to pass private/decode key to shareSecret. This method would return Encr(B, Encr(A,secret)) instead. If user doesn't know Decr(A,secret), then we may decide to not encode the secret in the class in the first place and return (EncrB,(secret)) which would be only readable to that user... I am not really sure which angle/issue you are trying to solve here, can you clarify? – Jérôme Verstrynge Apr 28 '11 at 11:41
  • @JVerstry Yes, you'd need to safeguard the process of entering and passing keys. The problem I was attempting to solve is as follows: Let's say user 1 and user 2 have several secrets and each uses a different single key to access all of their secrets. The problem I was trying to solve is how user 1 and user 2 could share a single secret with each other without the application ever storing their keys. Any methods for re-encrypting something need the decode key and the new key, thus the reason I suggested a temp key in the middle that is the only one that is stored. – Briguy37 Apr 28 '11 at 13:47
  • @JVerstry In a "Controlled Environment" where users can run "untrusted code trying to break our 'safe'", I still think you'd be better off with encrypting the secret, as a user could run the code pointed out by WhiteFang34 to access the secret. – Briguy37 Apr 28 '11 at 14:57
  • @JVerstry Also, in an "Uncontrolled Environment" where a hacker gets to start the app, since no secrets would be in there, there would be nothing to protect. – Briguy37 Apr 28 '11 at 15:11
0

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:

PrivateCredentialPermission SecurityPermission AuthPermission

These can help improve and control access to functionalities when implementing a cryptography system. They are necessarily sufficient to make the system secure.

Jérôme Verstrynge
  • 57,710
  • 92
  • 283
  • 453