Mike Liu
Mike Liu

Reputation: 149

Ruby on Rails: Is generating obfuscated and unique identifiers for users on a website using SecureRandom safe and reasonable?

Internally, my website stores Users in a database indexed by an integer primary key.

However, I'd like to associate Users with a number of unique, difficult-to-guess identifiers that will be each used in various circumstance. Examples:

As I see it, the key characteristics I'll need for these identifiers is that they are not easily guessed, that they are unique, and that knowing the key or identifier will not make it easy to find the other.

In light of that, I was thinking about using SecureRandom::urlsafe_base64 to generate multiple random identifiers whenever a new user is created, one for each purpose. As they are random, I would need to do database checks before insertion in order to guarantee uniqueness.

Could anyone provide a sanity check and confirm that this is a reasonable approach?

Upvotes: 1

Views: 423

Answers (2)

Vivien Barousse
Vivien Barousse

Reputation: 20875

The method you are using is using a secure random generator, so guessing the next URL even knowing one of them will be hard. When generating random sequences, this is a key aspect to keep in mind: non-secure random generators can become predictable, and having one value can help predict what the next one would be. You are probably OK on this one.

Also, urlsafe_base64 says in its documentation that the default random length is 16 bytes. This gives you 816 different possible values (2.81474977 × 1014). This is not a huge number. For example, it means that a scraper doing 10.000 request a second will be able to try all possible identifiers in about 900 years. It seems acceptable for now, but computers are becoming faster and faster, and depending on the scale of your application this could be a problem in the future. Just making the first parameter bigger can solve this issue though.

Lastly, something that you should definitely consider: the possibility for your database to be leaked. Even if your identifiers are bullet proof, your database might not be and an attacker might be able to get a list of all identifiers. You should definitely hash the identifiers in the database with a secure hashing algorithm (with appropriate salts, the same you would do for a password). Just to give you an idea on how important this is, with a recent GPU, SHA-1 can be brute forced at a rate of 350.000.000 tries per second. A 16 bytes key (the default for the method you are using) hashed using SHA-1 would be guessed in about 9 days.

In summary: the algorithm is good enough, but increase the length of keys and hash them in the database.

Upvotes: 3

Wawa Loo
Wawa Loo

Reputation: 2276

Because the generated ids will not be related to any other data, they are going to be very hard (impossible) to guess. To quickly validate there uniqueness and find users, you'll have to index them in the DB.

You'll also need to write a function that returns a unique id checking the uniqueness, something like:

def generate_id(field_name)
  found = false
  while not found
    rnd = SecureRandom.urlsafe_base64
    found = User.exists?(field_name: rnd)
  end
  rnd
end

Last security check, try to check the correspondance between an identifier and the user information before doing any changes, at least the email.

That said, it seems a good approach to me.

Upvotes: 2

Related Questions