Tuesday, October 17, 2006

Where Did I Put My Encryption Keys?

As a software security consultant, I was asked to solve some interesting problems with cryptography in Java. I warn others that security is esoteric and an afterthought much of the time. So taking security into consideration early is better. But even if you are a late-comer, the solutions do not cost that much, especially when compared to losing valuable data to an unfriendly attacker. Most of them can be done with Java packages that come with the JDK (the .NET framework has them too).

For example, a super easy solution to a common problem is password hashing. Passwords need to be hidden from attackers, programmers, admins or DBA's. This can be easily done with the MessageDigest class in Java. The great thing about hashing is that there is no key to store, you simply run your password through the algorithm, like SHA, and out pops a byte array that you can store in the database or a text file. Then the next time you prompt for the password, you hash that password with the same MessageDigest function, compared it to the one you've got stored, and if they match, you are logged in. Attackers or admins cannot possibly guess what that hash is by looking at it. So there you have a 3-line maintenance-free security solution that will pass any security audit - it's the same technique most Unix systems use to store their passwords.

Software cryptography provides quite a few different solutions, and when you analyze them, you try to find the weak spot. The weak spot is usually the keys to a cipher like 3DES or AES. When you use a cipher, you have a key, and storing this key safely can be a big problem since you don't want them to be stolen or
compromised. Most security books point this out and then fall short of suggesting some solutions: e.g. this one from Microsoft Press tells you to put the key somewhere far away from the data, but the example code violates that very suggestion by putting the key and the data right next to each other in the Windows registry (with a disclaimer that says don't do this). So what hope does a novice software developer have to avoid making bad choices when storing the keys? Other books on secure software are no better, I think their publishers forbid them to give good examples for fear of being sued.

Bad practices happen, but I will suggest some better ones since I have no publisher to forbid it. Let's define some security requirements for cryptographic keys that every developer should follow:
* The keys should be hidden
* The keys should not be known to the programmer
* The keys should be changeable after installation in case the keys are lost to an attacker
That's all we need, really. Now lets try to meet these requirements.

Solution 1: Hard-coding the keys in the code. Many developers will create a static String in the code that has encryption keys in it. This gets compiled and shipped with the software. Since the customer normally gets binaries and not source code, this hides the keys pretty well to satisfy the requirement above. However, the next two requirements are impossible with this solution. Now the programmer knows the keys - imagine being kidnapped by spies and tortured for the keys, or more likely, imagine being bribed for the keys. Then imagine what happens if someone bribes a programmer and then posts the keys on a public web site. Even easier than bribery, an attacker might download the software from your own web site, decompile it (amazingly simple for Java or C#), and then post your keys to a web site. You must panic, update the code, and every one of your customers must upgrade ASAP. This is a nightmare. You might say, "Yes, but most people would not know how to decompile the code, right?" Remember, you are not protecting your keys from MOST people, you are protecting them from a motivated attacker, and motivated people DO know how to decompile your code. (Yes, of course I know how to do it, but I won't bother putting the details here.)

Solution 2: Putting the keys in a config file. Storing your keys in a config file seems unpleasant because the keys are right there in plain text for all to read. Clearly this violates the hidden requirement above, doesn't it? Its true - its not hidden very well. However, the config file DOES satisfy the other two requirements: the keys are hidden from the programmer and they are changeable later. So now when the keys are lost to an attacker who posts them on a web site, you tell the customer to change the config file to enter new keys. The other customers need not worry, and the customer who lost their keys will be secure again in seconds.

Well maybe we can encrypt the config file! Yes, then we solve the hidden requirement! Brilliant! Except for one thing - where do you store the key for THAT encryption? Hard coded in the app? In another config file? You can see where this is going... The truth about security solutions is that they can all be beaten, some solutions are simply better than others and cause the attacker to expend more effort. In the case of the keys in the config file, you can simply use Unix or Windows file permissions to limit the readability of the file to the people who need to know, thus limiting the attackers who can get the keys. Then if the keys end up on a hacker web site, you change the keys, then haul off the admins to jail and torture them - but at least the programmers are safe and your customers are safe.

Some companies actually have a security policy that says "no storing passwords in plain text on the disk". I would say that this rule is impossible to follow. Because when I encrypt the password, where would I store the keys for that? Its a rule made by people who haven't thought about it. Sometimes this rule leads to hard-coding the keys in the code, and that's even worse. But if you have to follow this silly rule, use ROT13 since it needs no keys itself.

You can further protect the keys by putting them somewhere else separate from the data - maybe store them in a JNDI server instead of a config file (note that JNDI would persist to a text file, but at least it would be on a separate machine), or in a separate database from the rest of the data. (BTW: where did you store your database password? I suggest JNDI data sources, follow Solution 2 above). Anything to limit the exposure of the keys so you can hide them from most, but not all.

You can see that hard-coding the keys is appealing because it hides the keys, and what's more important than that? But hard coding the keys totally ignores the other two concerns, and it doesn't really do a great job of hiding the keys. So storing the keys in a config file (or other external place), even in plain text, is always preferred.

-Jay Meyer

jmeyer at harpoontech dot com

3 comments:

Anonymous said...

Using a hash routine (md5, sha, etc) is NOT a good idea for passwords. It's too easy to use a lookup table of hash values to find matches; the net also contains many lists of dictionary words pre-hashed so a simple google search could get results.

Even a salt would only slow down such a matching system; especially if the salt can be decompiled or stolen from the source code. However, using the username or id as the salt (so each row would get a different salt) would make the task more difficult.

Encrypting the password with the password is, I believe, a far better solution; however, a lot of users may use the same password (e.g., "password") and those rows would have the same encrypted value. To prevent this attack I would recommend xor'ing the username to the password before encrypting it thus ensuring even the same password would be encrypted differently in the database.

J (Encrypted Flash Drive Guy) said...

I think password hashing is the problem we ever faced. But I like the way of restoring passwords through algorithms. It is the best way to keep password and save it from the hackers.

USB Encryption said...

Answer to that question is hashing in the best possible way. If data to be hashed is very less then one can rely on any of the available techniques. They can fully satisfy the security needs. We should focus our attention towards hashing with less problemistic collisions rather than destroying our valuable time in removing collisions. By keeping this strategy in mind we can fulfill our goals.