手动生成碰撞免疫混淆 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)
重复碰撞的机会。
2
是 SecureRandom.hex
使用的位长度(它给出的长度为 4)。
唯一性验证不会提供任何额外的值,因为它只是检查是否存在具有该值的记录,而您已经在回调中检查了它。应用程序级别验证也容易出现您试图应对的竞争条件。
相反,这应该由数据库中的唯一索引强制执行。
您还可以创建一个 Postgres 函数并使用它生成一个随机字符串并在触发器中使用它来设置值。但是当性能实际上是一个问题时,我会从这个开始并研究这个。
我正在尝试手动生成一个小散列来混淆我的 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)
重复碰撞的机会。
2
是 SecureRandom.hex
使用的位长度(它给出的长度为 4)。
唯一性验证不会提供任何额外的值,因为它只是检查是否存在具有该值的记录,而您已经在回调中检查了它。应用程序级别验证也容易出现您试图应对的竞争条件。
相反,这应该由数据库中的唯一索引强制执行。
您还可以创建一个 Postgres 函数并使用它生成一个随机字符串并在触发器中使用它来设置值。但是当性能实际上是一个问题时,我会从这个开始并研究这个。