22

On our views in a Java web application, currently I am using hashCode as Id's for objects so that at server end I can get the same object back.

However, I am wondering how secure Java's hashCode really is so that someone cannot hack it to retrieve other person's objects. I am not very inclined towards Encryption-Decryption mechanism as it causes much CPU.

What other fast yet secure mechanisms I can use ?

4
  • 3
    Did you benchmark proper crypto hashes or are you simply claiming it's too slow? Commented Mar 7, 2014 at 9:33
  • 5
    There is no guarantee that hashCode will generate unique identifiers. Check the documentation.
    – Adam Arold
    Commented Mar 8, 2014 at 1:30
  • "Is Java `hashCode() secure?" = "Is using a polynomial as a hash function secure?" Commented Mar 8, 2014 at 4:03
  • 4
    Sorry, but this question is ... very mindboggling. I need a glass of water
    – mafu
    Commented Mar 8, 2014 at 20:13

4 Answers 4

55

You're breaking one of the hashCode() taboos; you're using its output as a key identifier. That's wrong.

You see, the output of hashCode() (in its default implementation) is a 32-bit unsigned integer, that's roughly 4 billion unique hashCodes. Sounds quite a lot? Well, not so much. Applying the birthday problem in this case shows as that with about 77000 objects, you have about 50% chance of collision. 50% chance of two objects having the the same hashCode.

Another issue is that the implementation of hashCode() can change from one Java version to the other. It's not meant to be permanent identifier of an object, so there's nothing forcing it to be consistent across versions.

If you insist on using hashes as object identifiers, then it's much better to have your own method instead of hashCode() to use for your key identifiers (for example, getMySpecialHashKey(). You can uses something like MessageDigest.getInstance("SHA-256") to digest the object into a nice 256-bit key.

My recommendation: Ditch the whole idea of hashing the object and rather generate a random identifier for your object when you construct it. Something along the lines of (in Java)

SecureRandom secRand = new SecureRandom();
byte[] objIdBytes = new byte[16]; //128-bit
secRand.nextBytes(objIdBytes);
String objId = Base64.encodeBase64String(objIdBytes); //Here's your ID

You also seem to bind access to an object only to knowledge of its key. That's also wrong. A proper permission-based model with proper authentication and authorization is needed.

16
  • 4
    There's nothing forcing hashCode() to be consistent even across different JVM invocations.
    – ntoskrnl
    Commented Mar 7, 2014 at 12:46
  • 3
    "If you insist on using hashes as object identifiers" DON'T.
    – o0'.
    Commented Mar 7, 2014 at 13:46
  • 4
    Good answer. A graph showing the probability of at least one hash collision in a set of n objects is here: blogs.msdn.com/b/ericlippert/archive/2010/03/22/… Commented Mar 7, 2014 at 17:40
  • 4
    +1 good answer. It might be more straight forward to use UUID.randomUUID() instead of SecureRandom to generate an ID, however. Commented Mar 7, 2014 at 18:50
  • 3
    @BenVoigt I suggest you take an introductory crypto course before calling the recommendations of others "terrible".
    – user10211
    Commented Mar 9, 2014 at 17:29
31

Java hashCode() was never intended to be used like this. Don't do ever it! It is even legal (while not recommended) for all instances of a class to return the same hashCode. The contract in Java is "two objects that are considered equal must have same hashcode". No more, no less. It would for example be valid to return hashcode 1 for all uneven numbers and 0 for even ones.

hashCode is used for faster lookup in Collections such as HashMap and collisions are expected.

Many classes define their own version of hashCode(), and if you know the code, you can often easily tell or guess the hashcode.

So, this is absolutely insecure.

1
  • The way I like to look at it, the purpose of hash codes is to speed up some (hopefully most) comparisons between things that are different, since anything whose hashcode doesn't equal X's hashcode can be regarded as unequal to X without even having to look at it. Not all classes can implement their hashcode in a way that allows any speed-up, but hashcode exists to allow those which can implement it usefully to do so.
    – supercat
    Commented Mar 7, 2014 at 18:47
12

It is not cryptographically secure, because that's not part of its requirements.

How the hashCode is implemented is an implementation detail up to the JVM and can be different for different object types (objects can override the hashCode method and provide an own implementation).

The purpose of the Java hashCode is to identify objects when placing them in hash tables. Its main requirement is performance. It also has a length of just 32bit, which is too short to avoid collisions. For its intended purpose (hash tables) two objects having the same hash is only a minor problem, but to identify objects without ambiguity collisions are a big problem.

But you should generally not allow any user to access any object just because they know the objects hash-code. Use a proper rights management scheme. Use password-protected user accounts where each user has a different set of permission-parameters. When the user tries to access an object, check if its permissions allow them to do so and reject the request when they don't.

2

It certainly isn't secure enough for a situation where guessing values can reveal information.

At least some of the overrides aren't even secure enough to use as a hash-code in the face of arbitrary user-selected input (it's really easy to do a hash-DoS attack against anything using Java's String.hashCode() override).

In cases where you want an identifier, I recommend taking a simple identifier (e.g. a numeric key in a database, an atomically incremented integer which is a key into a hash table, etc), and then appending a cryptographic hash of that and a salt.

That is, your publicly-exposed ID would be of the form (in language agnostic form):

Concatenate(PrivateID, HexString(SHA256(UTF8Bytes(Concatenate(Salt, PrivateID)))))

(You could remove part of the hash for a smaller ID at the cost of reduced time to brute-force).

Then the private ID can be obtained as a substring, and the full id can be verified again. The general procedure is also easily ported between languages.

The handful of CPU cycles this will cost isn't going to be a big impact relative to the rest of the system, unless your system is so trivial that you won't be using much CPU anyway, so unless you're running this on something really low-powered (as in, far less powerful than a cheap phone) it won't be a concern.

In cases where you actually want to use a hash-code as a hash-code and have to deal with user-selected input, then use a seedable hash algorithm (e.g. SpookyHash) and base your seed on start-up time (or better still, a mix of start-up time and the time you create the hash container). (Nothing to do with cryptographic hashes, so they can be much faster; the only security risk this avoids is hash-DoSing).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .