数据库级别的 UUID 用作安全措施而不是真正的权限控制?

UUID on database level used as a security measure instead of a true rights control?

数据库级别的 UUID 是否可以用作安全措施而不是真正的权限控制?

考虑一个 Web 应用程序,其中所有 servlet 都通过将会话 ID 连接到调用它的用户(通过 Web 客户端)来实现 "normal" 访问控制。因此,所有用户都经过身份验证。

需要的下一个安全级别是经过身份验证的用户是否确实 "owns" 正在更改数据。例如,在 Web 应用程序中,这可能是编辑表单中的一些文本。客户端确保用户不会意外地做错事(Java脚本)。问题当然是任何数量的网络工具都可以轻松地重复浏览器发出的调用,并且仅通过更改 ID,就可以在数据库中编辑不同的行 table 在用户没有 table 的 servlet 后面 "own".

我的问题是使用 UUID 作为数据库中的键是否足够 table 从而几乎不可能猜测有效 ID (https://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates)? As far as I know similar approaches is used in Google Photos (http://www.theverge.com/2015/6/23/8830977/google-photos-security-public-url-privacy-protected) 但我不确定它是 100% 可比的。

另一个选项是关闭的原因是让每个 servlet 验证用户只对自己的数据执行操作,但在具有 200 多个 servlet 和 50-100 tables 的大型应用程序中,这可能是一项非常繁琐的任务,很容易出错。在我看来,这会大大削弱安全性,但我不确定这是不是真的。

我倾向于 UUID 解决方案,但我也很好奇是否还有其他明显的方法可以解决我应该考虑的问题。

更新:

我可能应该澄清一下,我的计划是使用应该是随机的 UUIDv4。我知道关于 UUID 的实际随机性,熵在这里发挥作用,但据我所知,Java(这是选定的 platform/language)使用 SecureRandom,它应该是"cryptographically strong" (link).

在这种情况下,wiki 指出 (link): 换句话说,仅在接下来的100年内每秒生成10亿个UUID后,仅创建一个副本的概率约为50%。

以这种方式使用 UUID 有两个主要问题:

  1. 如果没有额外的身份验证方法,任何攻击者都可以简单地猜测 UUID,直到找到属于其他人的 UUID。 Google Photos 不需要太担心这个,因为它们只使用 UUID 来混淆 publicly 共享的照片视图;您仍然需要进行身份验证才能修改照片。这特别危险,因为:
  2. UUID 旨在 unique, not random。您的 UUID 中可能存在可预测的模式,攻击者可以观察并利用这些模式。此外,即使没有明确的模式,随着用户群的增长,攻击者需要测试以找到有效 UUID 的数量也会迅速减少。

我将始终推荐使用安全、持续检查的身份验证。但是,如果您的用户群相当小,并且您只是使用它来混淆 public 数据访问,那么以这种方式使用 UUID 可能没问题。即使那样,您也应该使用实际的随机字符串,而不是 UUID。

您希望密钥不可猜测,从而试图绕过授权控制。这是安全禁忌。根据您询问的对象,他们可能将其称为 insecure direct object reference or a violation of the complete mediation principle.

正如 F. Stephen Q 所指出的,您假设 UUID 是唯一的并不意味着它们不可预测。这里的威胁是,如果用户知道几个 UUID,比如说他自己的,这是否允许他预测其他人的 UUID?这是一个非常真实的威胁,请参阅:Cautionary note: UUIDs generally do not meet security requirements。特别注意 UUID RFC 所说的内容:

Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access), for example.

您可以使用 UUID 作为密钥,但您仍然需要进行授权检查。当用户想要访问他的数据时,数据库应该识别数据的所有者,服务器逻辑需要强制当前用户与数据库声明的所有者相同。

Another option is off cause to have every servlet verify that the user is only performing an action on its own data, but in a big application with 200+ servlets and 50-100 tables this could be a very cumbersome task where mistakes could easily happen. In my mind this weakens the security far more, but I'm not sure if that is true.

对于大型遗留应用程序,稍后添加安全性始终是一项复杂的任务。你是对的——应用程序越复杂,验证安全性就越难。复杂性是安全的主要敌人。

但是,这是最好的方法,而不是试图掩盖 insecure direct object reference 问题。

如果您在查询字符串中使用这些 UUID,那么 URL 秒内的这些信息可能会记录在不同的位置,包括用户的浏览器、Web 服务器以及两个端点。 URLs 也可能会显示 on-screen,由用户添加书签或通过电子邮件发送。当任何 off-site 链接被跟踪时,它们可能会通过推荐人 header 披露给第三方。将直接 object 引用放入 URL 会增加它们被攻击者捕获的风险。应用程序的现有用户随后被撤销了对某些数据位的访问权限——他们仍然可以通过使用先前添加书签的 URL(或通过使用他们的浏览器历史记录)来访问这些数据。即使在 URL 机制之外传递 ID,知道(或已经弄清楚)您的系统如何工作的本地攻击者也可能有意保存 ID 以备不时之需。

正如其他答案所说,GUIDs/UUIDs are not meant to be unguessable, they are just meant to be unique。诚然,Java 实现确实会生成加密安全随机数。但是,如果此实现在未来的版本中发生变化,或者如果您的系统移植到此功能不同的其他地方怎么办?如果您打算这样做,您还不如使用您自己的实现来生成您自己的加密安全随机数以用作标识符。如果你的标识符中有 128 位的熵,任何人都不可能猜到它们(即使他们拥有世界上所有的计算能力)。

但是,出于上述原因,我建议您实施访问检查。