1

I want to built some short urls for uploaded files and links on my own server. Simple enough, I upload files and my script returns me a link. On access, the link is translated to the file and download starts. Because it's modern, I want my links short and not so UUID4-like, because they are very long.

I do not want that someone tries a few URLs to found a valid link. Maybe I should sent only 404's if an IP already had 10 wrong urls called?

To built short random strings I thinking about the following:

  1. Prefill a SQL-Table (key, target) where key is case sensitive with all possible values in a defined range like [a-zA-Z0-9_-~=]{12}. Then select with WHERE target IS NULL ORDER BY RANDOM() an empty key and use it. This will guarantee I never generate the same key twice but needs a lot of storage and may be too much.
  2. I generate an UUID4-string and throw an hash-algorithm like SHA3 over it and use the first 12 characters of the result. Then I try to insert this in my table. If this fails, I need to generate an new UUID4 and so on...

Of course, these are very theoretical thoughts, because I think I will be fine with 1000 keys per year. But how shortening url services do this? Or they don't care about "unpredictableness"?

2 Answers 2

4

If you want unpredictable then you need unpredictable. If you start with an UUID and then play games with hash functions, you don't get unpredictable values; you just get values that "look randomish".

So just use a cryptographically secure PRNG. This means /dev/urandom (or anything similar, e.g. java.security.SecureRandom if you happen to be developing in Java).

This simple command (works on any Linux system) generates random, unpredictable 12-character values:

dd if=/dev/urandom bs=9 count=1 2>/dev/null | base64 | tr '+/' '_-'

The used characters consist in letters (uppercase and lowercase), digits, '_' and '-'. Thus, they should fit nicely in URL (raw Base64 produces '+' and '/' signs, which can be problematic, but I fix that with the tr call). Entropy is 72 bits, meaning that the strings are part of a space of size 272. If you generate 1000 strings, then probability of hitting one of the strings is 1000/272, i.e. about 1 in 4.7 billions of billions (so the attacker had better be super-lucky).

Translation to any particular programming framework is left as an exercise. The really important point is to use a strong PRNG, not things which are merely meant to be "statistically random" like SQL's RANDOM() or UUID. This is security: we want cryptographic randomness, not statistical randomness, because we need to defeat an intelligent attacker.

2

I would go for some counter value and add some randomness to it and not store all the individual results. Storing all results sounds way to complicated. If you use a 4 byte counter and combine it with a 4 byte random generated value then base32 encode the result. With a 4 byte counter you can generate 4294967296 links and the 4 byte of randomness should be sufficient to prevent someone an easy guess. If you need to generate more links, use more bytes, if you need more randomness use more random bytes. Using a counter, you only need to store just one value. Instead of using a counter you can also use the current time in milliseconds. This is even easier and the chance of generating the same URL when you generate a number of URLs in a short time is very small since you always have the random value.

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