5

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

在我看来,有三种方法可以做到这一点:

简单随机数

算法:

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

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

魂斗罗

  • 需要针对可能的竞争条件进行事务处理(算法不是原子的)

UUID

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

魂斗罗

  • 我们希望将页面标题作为 URL 注释 ( "#{id}--#{page_title}) 的短 URL,UUID 会将此注释一直向右移动
  • 我猜 UUID 作为主键的连接性能会降低吗?

加密的 ID

算法:

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

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

魂斗罗

  • ID 列的大小永远不能改变
  • 如果有人能破解/猜出钥匙,一切都是徒劳的

时间戳

由@emboss 推荐

  • 简单的
  • 不会用完 ID

魂斗罗

  • 可能会产生碰撞(尽管需要测试它是否真的发生)
  • 也许有点猜不透

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

由@viktor tron 推荐

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

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

魂斗罗

  • 更改了很多在公共接口中使用 ID 的东西
  • 用户可能希望他们可以减少包含此类标题的 URL,但在这种情况下,这些 URL 将不再有效

我想我会使用第三个选项。还是有更多的反对意见?有没有更好的解决方案?我们使用 Ruby on Rails 3.x 和 PostgreSQL 9.x。

编辑:未列出并不意味着私有!这意味着就像 YouTube 上的未列出的视频。它们是普通视频,只是没有在搜索或上传者的个人资料中列出。因此,您无法真正找到它们(无需尝试所有可能的 ID),但知道 URL 的每个人都可以访问它们。当然,将某些内容设为未列出并将链接发送给其他人的用户必须意识到它可能不会保持未知状态(URL 可能会被传递,并且通过链接可能最终会出现在搜索引擎中)。

我们还有另一种选择将事情保密。这是两个不同的东西。(我认为假设每个人都知道“未列出”的含义是错误的。)

4

6 回答 6

12

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


这是对错误问题的错误解决方案。

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

实际问题是:用户可以在未经授权的情况下访问事物。

设置授权逻辑,只允许用户访问他可以合法访问的项目并禁止其他一切。

隐藏数据库中有多少条目

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

于 2012-05-28T14:24:44.493 回答
3

我建议采用完全不同的方式:根本不向用户显示记录 ID。你不需要。对 url 使用另一种形式的标识。

既然你说你想要漂亮的网址,你可以简单地使用一个 slugger/permalink gem,比如https://github.com/norman/friendly_id

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

说真的,别管 ID :)

于 2012-05-28T14:17:22.113 回答
2

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

在 Ruby 中,执行以下操作:

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

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

于 2012-05-28T14:33:01.410 回答
1

我认为 Sergio 已经为您的问题提供了完美的答案。

您试图实现的是一个很好的隐匿安全示例:您不是正确限制对某些未列出项目的访问,而是试图对人们隐藏这些项目。但这仍然留下了猜测那些隐藏项目的可能性,而访问限制使得无法查看不应该查看的页面。这就是为什么访问限制是明显的赢家:我们看到不应该看到的东西的可能性为 0,而成功的可能性很小。即使它可以忽略不计,0 也总是会胜过某个大于零的值。

我只是想添加一些想法,为什么您提出的解决方案不起作用:

随机数

不在SecureRandom这里使用 a 已经破坏了目的。使用法线rand可以使随机数可预测,因此任何决心“找到”隐藏页面的人都有很大的成功机会。但是,即使使用安全的随机数,您也只是将页面均匀分布在某个数字范围内。最终进入您的应用程序的记录/页面越多,攻击者简单地随机猜测最终成功的可能性就越高。

UUID

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

加密/散列

在这里使用加密是错误的。从某种意义上说它是可逆的,这是错误的,而且没有必要这样做,因为这正是你真正想要阻止的。除非您使用经过身份验证的加密,否则生成的密文将具有延展性,因此即使不知道所使用的密钥,攻击者也很有可能在被禁止的页面上欺骗他的方式。更不用说他们可能会尝试恢复密钥的无数次攻击。因此,更好的解决方案确实是使用适当随机化的安全散列函数。使用静态盐也不够好,原因与它对密码不够好一样:每个 ID 的盐会更好地减少预先计算字典的能力。预计算非常简单,用各种盐计算 ID 为 1-100 的表实际上是一个很有前途的策略,

但无论你多么努力,总有机会通过简单的猜测获得访问权限。因此,总结一下 Sergio 已经说过的话,您真正需要的是身份验证和访问限制的实现。

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

于 2012-05-28T15:35:17.900 回答
1

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

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

于 2013-02-10T07:40:24.077 回答
0

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

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

于 2012-05-28T15:04:53.070 回答