Rails,Ruby:SecureRandom.urlsafe_base64需要检查令牌的唯一性吗?

9
我需要在我的DB中为用户存储唯一的令牌。目前,当我生成一个令牌时,在使用它之前,我会在DB中检查其唯一性。这是我实际需要执行的测试吗?还是我在浪费时间?
我已经查看了Ruby 2.0.0 API for SecureRandom,但它并没有说明我是否可以“信任”唯一性。
我知道没有随机值可以真正地“独特”,并且存在有限数量的可能性。但是,使用32位十六进制值,我确信我永远不会在我的应用程序中再次遇到相同的值,但想问问是否有人知道这种情况的“陷阱”。
另一个考虑因素是使用SecureRandom.uuid,但本质上将是相同的情况。
# 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
3个回答

14

SecureRandom.uuid 生成uuid。UUID长度为128位,可保证在空间和时间上的唯一性。它们旨在全球唯一,不同于urlsafe_base64。请参阅RFC4122


@tompave发布的答案也是可行的。然而,使用SecureRandom.uuid生成器不需要任何额外的工作,因此我将其标记为我特定问题的最佳解决方案(“保证”唯一令牌的方法)。UUID规范4“应该”也有122个随机位(据我所读),因此它不仅是唯一的,而且还在于用户不能轻易地“猜测”另一个UUID。 - Dan L

6
它并不能确保唯一性,但是像svoop所说的那样,你极有可能不会得到两次相同的结果。
我的建议是:如果你只需要随机、唯一和无法预测的令牌,并且你没有成千上万的用户,请放心使用它。
如果你绝对需要唯一的令牌(例如有法律要求),那么请将与用户相关联的唯一字段(例如用户电子邮件)与一个随机盐结合起来,然后对结果进行哈希运算。
一种简单的实现方式是:
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

这是一个好的解决方案。但是我选择了SecureRandom.uuid实现,因为这样做需要我付出更少的努力,同时仍然确保唯一性和安全性 :) - Dan L
我更喜欢这个方案,更加安全。 - JellyFishBoy

4

不会的,你在一生中都不会看到重复。

32 是在将随机数生成之前的长度(以字节为单位),然后将其转换为url安全的base64字符串,因此出现重复的几率大约是1/10'000'000'000'000'000'000'000'000'000'000。这是10e31,而宇宙只有43e17秒的历史。


并不是100%准确。我在生产中刚刚遇到了一个重复项。我只是幸运吧? :) - djb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接