手动生成碰撞免疫混淆 slug

Generating a collision immune obfuscating slug manually

我正在尝试手动生成一个小散列来混淆我的 webapp ID

(我已经解释了为什么我手动执行此操作而不是使用真正的哈希,例如 SHA、MDA 或什至是编码哈希,例如此线程中的 hashid:hashids vs pure random hash in id obfuscation

这个散列是纯随机的:我从字母数字字符串中挑选一些数字:

"abcdefghijklmnopqrstuvwxyz1234567890"

这是我的代码:

model.rb

class Model< ApplicationRecord
  before_save :set_hashid
  validates :hashed_id, uniqueness: true

  private

  def set_hashid
    chain = "abcdefghijklmnopqrstuvwxyz1234567890"
    numberdigits = 4

    until @hash.present? && !Model.exists?(hashed_id: @hash)
      @hash = ""
      numberdigits.times do
        @hash << chain[rand(0..chain.size-1)]
      end
    end

    self.hashed_id = @hash
  end

end

效果很好。创建一个由 chain 字符串中的 4 位数字组成的散列。它给出了 36^4= 1.679.616 种可能性

我的模型足够了(最终应该达到 1.000.000)

当在字段 hashed_id return 中搜索模型时,哈希通过 false。

尽管有两个问题:

1st : 我的数据库是空的,所以速度非常快。但是当该列有 100 万条记录时,索引列中 4 位哈希的搜索时间是多少?有没有一种方法可以预测这一点(除了找到一个我可能很难找到的类似的假人 table)

2st : 我使用 uniqueness 强制执行验证,所以我想搜索此哈希是在我的 set_hashid 中进行的,但在执行时也会执行验证是多余的。尽管我热衷于保持验证,以防类似的哈希同时通过另一个用户的测试(非常非常不可能,但谁知道)。有没有一种方法可以做不同的事情,这样就不会进行额外的搜索并保留验证?

require 'secure_random'
class Model < ApplicationRecord
  before_validation :set_hashid!, unless: -> { self.hash_id }
  def set_hashid!
    begin
      self.hashed_id = SecureRandom.hex(2)
    end while self.class.exists?(hashed_id: self.hashed_id)
  end
end

begin ... while condtion 运行 块至少一次,并且会 运行 self.hashed_id = SecureRandom.hex(2) 重复碰撞的机会。

2SecureRandom.hex 使用的位长度(它给出的长度为 4)。

唯一性验证不会提供任何额外的值,因为它只是检查是否存在具有该值的记录,而您已经在回调中检查了它。应用程序级别验证也容易出现您试图应对的竞争条件。

相反,这应该由数据库中的唯一索引强制执行。

您还可以创建一个 Postgres 函数并使用它生成一个随机字符串并在触发器中使用它来设置值。但是当性能实际上是一个问题时,我会从这个开始并研究这个。