随机化DB记录ID

问题描述 投票:5回答:6

在我们的Web应用程序中,我们希望随机化记录ID。原因是我们想要隐藏数据库中已有多少条目,我们还有未列出的内容。如果ID是简单的增量数字,则很容易猜出未列出的内容的ID。

我认为有三种方法可以做到这一点:

简单的随机数

算法:

  1. 在插入时创建一个随机数。
  2. 检查ID是否已被使用。如果是,转到1。
  3. 使用此ID。

Pro

  • 简单
  • 适用于任何大小或类型的ID(32位,64位,变长,字符串)

Contra

  • 需要针对可能的竞争条件进行交易(算法不是原子的)

的UUID

Pro

  • 碰撞的可能性非常低,你可以忽略它

Contra

  • 我们希望将页面标题作为URL注释("#{id}--#{page_title})获得不错的短URL,UUID会将此注释一直转移到右侧
  • 我猜UUID作为主键会在连接上有较低的性能吗?

加密的ID

算法:

  1. 使用nextval(原子!)从序列中读取数字
  2. 使用密钥和加密算法加密ID,加密算法使用ID的大小

Pro

  • 没有竞争条件(不需要交易)

Contra

  • ID列的大小永远不能更改
  • 如果有人可以破解/猜测密钥都是无用的

时间戳

由@emboss推荐

Pro

  • 简单
  • 不会用完ID

Contra

  • 可能会产生碰撞(虽然需要测试它是否真的发生)
  • 也许有些可猜测

随机公共ID /基于名称的公共ID

由@viktor tron推荐

URL中出现的所有内容的第二个ID,仅用于查找记录。使用内部正常ID(用于连接等)。

Pro

  • 内部一切都保持清醒
  • 一个好的随机算法/命名方案应该让URL猜测不可能(足够)

Contra

  • 改变很多在公共接口中使用ID的东西
  • 用户可能希望他们可以删除带有此类标题的网址,但在这种情况下,网址将不再有效

我想我会使用第三种选择。还是有更多反对它的论据?有更好的解决方案吗?我们使用Ruby on Rails 3.x和PostgreSQL 9.x.

编辑:不公开并不代表私人!它就像YouTube上的不公开视频一样。它们是普通视频,未在搜索或上传者的个人资料中列出。所以你无法真正找到它们(没有尝试所有可能的ID),但知道URL的每个人都可以访问它们。当然,制作未列出的内容并将链接发送给其他人的用户必须知道它可能不会保持未知(URL可能会被传递并且通过链接可能最终会出现在搜索引擎中)。

我们还有另一种选择,可以将事物设为私有。这是两件不同的事情。 (我认为假设每个人都知道“未上市”意味着什么是错误。)

ruby-on-rails postgresql encryption random uuid
6个回答
12
投票

注意:这回答了问题的初始版本,从中不明显这不是授权逻辑的替代品。


This is a wrong solution to a wrong problem.

您认为问题是:用户可以猜测“未列出”事物的ID并使用它们。

实际问题是:用户可以未经授权访问。

将授权逻辑放在适当的位置,允许用户只访问他可以合法访问的项目,并禁止其他所有内容。

Also

隐藏数据库中有多少条目

如果这是原因,我认为小的并不羞耻。无论如何,你可以从100000开始你的序列或增加N或使用另一个类似的技巧:)


3
投票

我建议采用完全不同的方式:只是不向用户显示记录ID。你不需要。使用其他形式的身份证明。

既然你说你想要漂亮的网址,你可以简单地使用一个重击手/永久链接宝石,比如https://github.com/norman/friendly_id

friendly_id的默认slug生成器提供了检查slug字符串的唯一性的功能,并在必要时附加一个序列来保证它。

说真的,单独留下ID :)


2
投票

使用具有一些静态盐的抗冲突哈希函数以及“内部ID”引用。例如,SHA-256将X中的元素唯一地映射到H中的元素,具有较低的碰撞概率;然而,从H中的元素计算X中的元素是非常困难的(数学上)。

在Ruby中,执行以下操作:

@hashed_id = Digest::SHA2.new << SHA_SALT << @foo.id

顺便说一下,这不是一种加密形式,因为任何人都可以在不知道私钥的情况下,在给定相同输入的情况下生成相同的哈希。它也只是一个单向函数,因此也没有“解密”算法。


1
投票

我认为塞尔吉奥已经为你的问题提供了完美的答案。

你想要实现的是默默无闻的安全性的一个很好的例子:你试图将这些项目隐藏起来,而不是正确地限制对某些不公开项目的访问。但这仍然有可能猜出那些隐藏的项目,而访问限制使得无法查看一个不应该的页面。这就是为什么访问限制是明显的赢家:我们有0次查看我们不应该拥有的东西而不是成功的可能性很小。即使它可以忽略不计,0总是会赢得大于零的某个值。

我只是想补充一些想法,为什么你提出的解决方案不起作用:

Random Numbers

在这里不使用SecureRandom就已经失败了。使用正常的rand可以使随机数可预测,因此任何决定“找到”隐藏页面的人都有很大的成功机会。但即使使用安全的随机数,您只是“扩散”您的页面均匀分布在某些数字范围内。记录/页面最终落入您的应用程序中,攻击者随机猜测最终成功的可能性就越高。

UUIDs

一旦攻击者发现它们是如何构建的,它们很容易猜到。由于它们是按照确定性方案构建的,因此它们的随机性没有任何安全性。

Encryption/Hashing

使用加密在这里是错误的。这是错误的,因为它是可逆的,并且没有必要,因为这是你实际上想要防止的。除非您使用经过身份验证的加密,否则生成的密文将具有可扩展性,因此即使不知道所使用的密钥,攻击者也很有可能在禁止的页面上进行操作。更不用说他们可能试图恢复密钥的众多攻击。因此,更好的解决方案确实是使用正确随机化的安全散列函数。使用静态盐是不够好的,原因与密码不够相同:每个ID盐可以更好地最小化预先计算字典的能力。预计算非常简单,使用各种盐计算ID为1-100的表实际上是一种很有前途的策略,因为攻击者知道它是在此处进行散列的顺序数据库ID。

但无论你怎么努力,总有机会通过简单的猜测获得访问权限。因此,总结一下塞尔吉奥所说的内容,您实际需要的是身份验证和访问限制的实现。

如果要隐藏数据大小,为什么不尝试使用时间戳呢?在任何情况下,我都会保持数据库ID不变,并为您要在URL中显示的内容添加一个特殊的“显示ID”列,但保留原始ID作为主键。


1
投票

无耻插头:https://github.com/dividedmind/pg_random_id

只需将gem放入其中,按照自述文件添加迁移即可。这是基于加扰序列,因此保证不会发生冲突。您可以使用随机整数或字符串ID。


0
投票

虽然这是一个值得称赞的目标,但将ID列从自动递增的整数更改可能是一个错误。当您接下来时,ID列应该仅供数据库使用。它允许数据库公开关系并确保记录可以彼此分开查找。您正在尝试使用ID列来展示我认为的业务逻辑:您希望模型基本上是随机的参考编号。当业务逻辑发生变化时,您将需要更改ID列,这将导致外键丢失,并且可能会非常令人头疼。

要实现这一目标,您应该创建一个名为“number”的新列,并在其上实现其中一个策略。然后,如果你需要迁移到一个新策略,那么这样做会容易得多:而不是做Model.find(id),你只需要做Model.find_by_number(number)

© www.soinside.com 2019 - 2024. All rights reserved.