7

In building a Java based system that needs unique identifiers on the URL, is UUID.randomUUID() or SecureRandom a better choice?

More specifically, a call to POST /items will return a 201 Created with a Location to the newly created /items/{id} where {id} is a random string and the URL is intended to be used by anonymous HTTP requests for a limited time window.

This code returns a 22 character string (e.g. B-KEPLFdWNZ4JTUnnEq3Og) with 128 bits of randomness.

SecureRandom random = new SecureRandom();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
String token = encoder.encodeToString(bytes);
System.out.println(token);

UUID.randomUUID() is a "Static factory to retrieve a type 4 (pseudo randomly generated) UUID. The UUID is generated using a cryptographically strong pseudo random number generator." This code returns a 36 character string (e.g. f0803ce7-8e3d-421a-8126-e5a0bd0a865f) with 122 bits of randomness.

UUID randomUUID = UUID.randomUUID();
System.out.println(randomUUID);

These identifiers will also be persisted in a relational database as a fixed length CHAR type.

Kevin Hakanson
  • 491
  • 1
  • 5
  • 13
  • [Related, but not a duplicate](http://security.stackexchange.com/questions/120352/should-i-use-a-cryptograpically-secure-random-number-generator-when-i-generate-i) since your question is about Java and my old one about Node. – Anders Oct 25 '16 at 07:37

2 Answers2

9

In terms of the raw amount of random bits, yes.

Looking at the source for Java's random UUID generation, you can see they actually utilize the SecureRandom class to generate the raw bytes. So, the 'quality' of your randomness isn't going to change.

The fact that you have 6 more bits does, technically, make it more difficult to brute force a duplicate. Wikipedia's article on UUID has a good description of the math behind it. To quote it:

[A]fter generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%

If these links are going to expire in, say, 24 hours, this is a very small thing to worry about, and that's only with 122 bits.

Now, is more bits going to make it harder for an attacker to brute force a collision? Sure! Is it worth it? Maybe... but maybe not. Only you can really decide what's right for your use case.

When you get to the point where brute forcing a collision would, on average, take longer than the time for the estimated heat death of the universe, it might be a little overboard. That's getting to the territory of needing to worry more about hackers compromising the security of your storage more than brute forcing (and, honestly, even with UUIDs, you're going to have that problem). Ultimately, the difficulty of brute forcing doesn't matter if they can find another way in. If you're really worried, storage is cheap - store 256 bit strings that you generate with a SecureRandom and call it good, but make sure you have proper security around everything else, or it's useless.

childofsoong
  • 458
  • 2
  • 11
8

Well, in principle, UUIDs and cryptographic RNGs promise two different things:

  1. UUIDs promise low probability of duplicates;
  2. Cryptographic RNGs promise unpredictability to a malicious adversary.

If #2 is a concern, I'd make sure to use a cryptographic RNG. For example, if you're concerned that a malicious attacker who can request a lot of UUIDs in a short timespan might learn enough information to predict those given out to other users' requests.

As it happens, Java's UUID.randomUUID() method does use a cryptographic RNG, so it should be safe in that regard. But the risk then is that developers who later look at your code may not understand that your intent includes secure cryptographic random choice, and not just uniqueness. For example, somebody might rewrite that particular module using some other language or UUID library that does not use a cryptographic RNG, and expose you to prediction-based attacks that you meant to exclude. So if cryptographic security really was an important factor I'd try to write the code in a way that makes that obvious, by using SecureRandom explicitly. (Security-critical code needs to be very clear!)

If all I cared for was uniqueness instead of security then I'd go ahead and use UUID.randomUUID().

Luis Casillas
  • 10,181
  • 2
  • 27
  • 42