【问题标题】:Generating cryptographic secure IDs instead of sequential identity / auto increment生成加密安全 ID,而不是顺序身份/自动增量
【发布时间】:2019-11-26 19:58:10
【问题描述】:

我一直处于这种两难境地,找不到任何提示,尽管似乎有人已经这样做了。

我需要用加密安全(即非连续!)ID 替换顺序 AUTO_INCREMENT(或等效)主键,但同时我想保持顺序 PK 的性能优势:保证未使用的下一个 ID,集群性等。

一种简单的方法似乎是实现一个加密伪随机排列生成器,以将 2^N 空间唯一地映射到 2^N,而不会发生冲突并具有初始化向量 (IV)。

虽然这可以在外部实现,但这确实需要存储和原子访问状态(排列位置或最后一个 id),这意味着在外部实现将非常低效(相当于为每个 @ 运行后续 UPDATE table SET crypto_id = FN_CRYPTO(autoincrement_id) WHERE autoincrement_id=LAST_INSERT_ID() 987654323@)。

您知道在商业用途的数据库中上述任何此类实现吗?

【问题讨论】:

  • 为什么不使用 uniqueidentifier?这还不够安全吗?请参阅下面链接中的示例 B:
  • @DaFi4 谢谢你,我不知道它存在;然而,似乎有一些怪癖:1)它是一个长的字母数字香肠,比数字 ID PK 处理起来要慢得多。 2) 阅读 RFC 4122,UUIDS 有 2 种实现:一种是随机的(会产生冲突),另一种是时间戳,以 0.1us 为增量,清晰(没有散列或任何东西)。所以不够安全被低估了……根本不安全。它使 id 在足够近的距离处保持递增顺序。此外,MS doc 没有说明它是哪种算法。 3) 这是否扩展到分布式数据库?
  • 4) 如何获取最后插入的ID?
  • 1.它并不是真的很慢,它的设计速度很快 2. 它的设计使碰撞不会发生。我在 22 年中从未见过一个。 3. 是的,事实上,使用这种方法的一个主要功能是解决分布式数据库和数据产生的问题

标签: sql database cryptography


【解决方案1】:

虽然这可以在外部实现,但这确实需要存储和原子访问状态(排列位置或最后一个 id),这意味着在外部实现将非常低效(这相当于运行后续

 UPDATE table SET crypto_id = FN_CRYPTO(autoincrement_id) 
 WHERE autoincrement_id=LAST_INSERT_ID()

您可以使用生成/虚拟列来避免为每个插入运行建议的更新:

-- pseudocode
CREATE TABLE tab(
   autoincrement_id INT AUTO_INCREMENT,
   crypto_id <type> GENERATED ALWAYS AS (FN_CRYPTO(autoincrement_id)) STORED
);

-- SQL Server example, SHA function is an example and should be replaced
CREATE TABLE tab(
 autoincrement_id INT IDENTITY(1,1),
 crypto_id AS (HASHBYTES('SHA2_256',CAST(autoincrement_id AS NVARCHAR(MAX))))     PERSISTED
);

db<>fiddle demo


更多信息:


迪努编辑

如果您使用 SHA,请不要忘记将秘密盐连接到 autoincrement_id;或者,您可以使用 AES128 来加密 autoincrement_id 并使用密码和 IV。

另外值得注意的是:任何可以访问 DDL 表的 DB 用户都可以访问您的密钥 salt/key/iv。如果您对此感到担忧,您可以使用参数化存储过程,即FN_CRYPTO(id,key,iv),并将它们与每个插入一起发送。

要在应用端检索crypto_id 而无需后续查询,您需要复制加密函数应用端以在返回的autoincrement_id 上运行。注意:如果使用autoincrement_id 作为 AES128 的字节数组,请非常注意字节顺序,它可能会在数据库和应用程序端有所不同。唯一的选择是使用 mssql 的 OUTPUT 语法,但这是特定于 mssql 的,它需要运行 ExecuteScalar API 而不是 ExecuteNonQuery

【讨论】:

  • 谢谢,这看起来在正确的轨道上,但是如何获取生成的 crypto_id 作为INSERT 的结果?请注意,我们使用的是 ORM 应用程序端,因此将存储过程伪装成 INSERT 包装器将是一个糟糕的主意,因为这意味着大量的代码更改。
  • 也许根据身份ID复制生成功能应用程序端可以获取加密ID?我会使用 AES128 而不是 hasing,因为它可以保证不会发生冲突,而且我看到它在其他地方实现,但它适用于大多数 RDB。
  • @Dinu how to get the generated crypto_id as a result of an INSERT 您可以在 RDBMS 中创建用户定义的函数并设置为计算列表达式。
  • 不,我的意思是我需要在应用层的INSERT 后面生成crypto_id。现在,大多数 RDB 只返回 AUTO_INCREMENT / IDENTITY 值。我看到 MS SQL 有一些 OUTPUT 语法来返回一些其他值,但它需要重写整个 ORM 才能使用 ExecuteScalar。不是很方便。
  • 我想我知道答案:将 OUTPUT 与 SCHEMABINDING 结合使用:stackoverflow.com/questions/6354894/…
【解决方案2】:

只是一个想法...数据库本身是否安全?如果是这样,您可能会考虑一个“密钥池”表,其中包含一个伪随机键列表和表中每个键的“状态”列。然后,您可以在需要时分配下一个键。如果可用密钥低于设定的阈值,则可以在空闲时间和/或基于触发器填充密钥池。

同样,此方法将取决于能否保护密钥池表,但它会确保分配的密钥是随机且唯一的。

此外,您需要确保不会产生并发问题,但这可以通过存储过程来完成,并且仍然应该比按需生成安全 ID 更快。

【讨论】:

  • 不,搜索“密钥池”然后更新所述密钥池并不比即时生成加密 ID 快。您提出的建议比我已经列出的效率低下(插入/更新)效率更低。
  • 我同意,总的来说,密钥池的效率较低。但我在想,可以在空闲时间重新填充池,以便将开销分配到不太关键的时间段。当然,我不知道您的应用程序的细节,所以我知道这对您来说可能不是一个实际的选择。
  • 在拉取每个 ID 后,仍需要更新密钥池,以将其标记为已消失。绝对没有理由创建密钥池,密钥生成并不慢。使用相应互斥锁重复搜索/插入/更新的数据库操作很慢。
猜你喜欢
  • 2010-10-24
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
  • 2013-03-02
  • 2016-09-27
  • 2021-05-09
  • 2017-08-11
  • 1970-01-01
相关资源
最近更新 更多