I need unique tokens for users stored in my DB. At the moment when I generate a token I check it for uniqueness in the DB before using it. Is this a test I actually need to perform or am I wasting time?
I've looked at the Ruby 2.0.0 API for SecureRandom and it doesn't clarify if I can "trust" the uniqueness or not.
I know no random value can really be "unique" and there's a finite number of possibilities. But with 32 bits of hex values I feel confident I will never run into the same value again in my app but wanted to ask if anyone knew of a "gotcha" with this situation.
Another consideration is using SecureRandom.uuid
but that will essentially be the same situation.
# usage
user.password_reset_token = Generator.unique_token_for_user(:password_reset_token)
# Performs DB query to ensure uniqueness
class Generator
def self.unique_token_for_user(attribute)
begin
token = SecureRandom.urlsafe_base64(32)
end while User.exists?(attribute => token)
token
end
end
SecureRandom.uuid
generates uuids. A UUID is 128 bits long, and can guarantee uniqueness across space and time. They are designed to be globally unique, unlike urlsafe_base64
. See RFC4122.
It doesn't ensure uniqueness but, as svoop said, it's extremely unlikely that you'll get the same result twice.
My advice is: if all you need are random, unique and unguessable tokens, and you don't have hundreds of thousands of users, then use it without worrying.
If you absolutely want unique tokens (e.g. there is some legal requirement), then combine a unique field associated with the user (e.g. the user email) and a random salt, and hash the result.
A naive implementation would be:
require 'securerandom'
require 'digest/md5'
def generate_user_token(user)
digest(user.email + random_salt)
end
def random_salt
SecureRandom.urlsafe_base64
end
def digest(string)
Digest::MD5.hexdigest string
end
No, you won't see a duplicate in your lifespan.
32
is the length (in bytes) of the random number generated before it get's converted to an urlsafe base64 string, so chances of a duplicate are roughly 1 to 10'000'000'000'000'000'000'000'000'000'000. That's 10e31 and the universe is only 43e17 seconds old.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With