【问题标题】:Database design - exposing Primary Key数据库设计 - 暴露主键
【发布时间】:2010-12-15 02:38:10
【问题描述】:

假设我有一个 Customer 表。

在这个表中有一个 ID 列,Primary Key int,Identity,所有这些爵士乐。我们的客户也有一个名字。

现在,如果我要在报告或电子邮件群发中发送信息,其中有一个链接返回到我的网站以唯一标识该客户,那么使用主键 ID 字段是否是一种好习惯?

我所有的直觉都告诉我这很糟糕,代理 ID 应该只用于数据库 - 并且永远不要以这种方式暴露给外界。

但我正在寻找我可以在未来使用的杀手级论点,如果(何时)提出这个案例。

想法?

【问题讨论】:

  • 您不使用ID的原因是什么? (本能不算数)
  • 我通常不会将内部 CustomerID 发送给客户,要求他们在打电话或类似情况时将其还给我。但如果该报告是公司内部的 - 为什么不使用 PK 呢?毕竟,唯一且可靠地识别数据中的单行的工作!

标签: database-design


【解决方案1】:

唯一标识符是唯一标识符,如果您的数据库表的主键中已有 on,请使用它。创建另一个代理键将是多余的,除非您有 GUID,例如,这对于您的输出格式来说太长了。

--- 编辑 ----

只是考虑邮件参考,在过去我已经创建了一个表来跟踪特定邮件以围绕响应建立指标。每条邮寄记录都使用 GUID 作为主键以保持其唯一性,并且该记录链接回客户记录,客户记录中的任何详细信息都不会呈现给链接的查看者。

如果您要提供客户详细信息,则需要进行访问控制,即使您混淆了 ID,任何掌握该链接的人都可以访问该数据,并且在大多数系统中这是不可接受的。

我认为决策始于“这些数据需要有多安全?”并从那里继续前进。

【讨论】:

    【解决方案2】:

    我建议不要添加新字段,但也不要公开 ID。

    创建一个表来存储您发送的电子邮件。包括 GUID 作为主键和 UserId 作为外键。在您提供给他们的 URL 中,链接回 GUID。然后,如果用户单击 URL,您可以根据 GUID 更新记录并指示用户何时访问 URL。

    【讨论】:

    • 您还可以为每个邮件创建一个新的 GUID,并能够对电子邮件发送和响应进行粗略分析。
    【解决方案3】:

    考虑到最终用户可能会发现如果他们能够更改 url,因为它们包含可读的字符串,而不仅仅是 id,他们会更容易浏览您的网站。这是一种更放松的方法。

    另一方面,为什么不公开 id 呢? 如果您的应用程序容易受到攻击,仅仅是因为有人推断出了主键,那么您就有问题了。

    【讨论】:

      【解决方案4】:

      可以使用您正在使用的表单中的密钥。但是,在您的特定情况下,存在可以使用 url 并且人们可以看到其他人的信息的问题。这取决于该网址上的内容是否对您来说是个问题。如果是,那么我建议您在您的 url 中添加一些盐(第二个数字、guid、客户电子邮件哈希等),然后您可以将其与 ID 匹配以验证 URL 是否正确。即使使用不是 1、2、3 的 ID ......而不是使用每 7 个 ID ......仍然是可以猜测的。另一方面,如果你愿意的话,你可以只拥有 ID……或者整个 URL。除非您真的想要它们,否则您的信息通常不会被外界消费。在这种情况下,您的直觉是正确的,因为数据不应该被人类消费。但是该 URL 是您系统的一部分,因此实际上只是使信息对阅读该 url 的人无用。

      【讨论】:

      • 我在此处添加此内容,但它适用于此处的许多回复。我认为这更像是整个站点的访问控制问题,而不是“密钥”的易读性。无论是身份、rowguid 还是文本名称,它们都容易被滥用并落入松散耦合应用程序的“不信任传入数据”方法,即验证真实性和访问数据的权限。如果您的系统完全开放并依赖于其中的混淆引用,那么当有人将您的数据剥离时,您只能责怪自己。 :)
      【解决方案5】:

      按照您描述的方式,听起来好像用户可以通过修改链接中的主键值来查看其他用户的数据。如果是这种情况,为外部目的生成唯一 ID(例如 GUID)将是更好的方法。

      【讨论】:

        【解决方案6】:

        使用代理键,因为您不希望人们能够猜测(并且随着 PK 的增加,这并不是真正的猜测)其他人的 ID 并看到他们的信息。我建议在您的客户表中添加一个键。

        以下是添加新列的示例代码,该列为每条记录生成一个 guid。

        BEGIN TRANSACTION
        GO
        ALTER TABLE dbo.Customers ADD
            PublicCustomerID uniqueidentifier NULL
        GO
        ALTER TABLE dbo.Customers ADD CONSTRAINT
            DF_Customers_PublicCustomerID DEFAULT newid() FOR PublicCustomerID
        GO
        ALTER TABLE dbo.Customers SET (LOCK_ESCALATION = TABLE)
        GO
        COMMIT
        

        代理密钥还可以很好地用于电子邮件爆炸,为非关键客户信息提供合理数量的保护。但是,如果它真的很重要,则需要通过身份验证系统进行保护,而不仅仅是混淆的客户 ID。但是,如果它是销售电子邮件爆炸,如果他们必须单击链接并登录才能查看页面而不是单击链接,那么您将降低转化率

        【讨论】:

        • 如果你的“安全机制”依赖于无法猜测一个简单的值,那么你根本没有真正实现安全
        • 你猜不出代理键吗?
        • 对于微不足道的保护,guid 将提供合理数量的保护。对于电子邮件爆炸,这应该足够了——因为敏感信息不应该通过截获电子邮件来获得。应该为该类型的数据使用身份验证机制。
        【解决方案7】:

        创建一个名为CustomerNumber的新字段,在系统中是唯一的,但与ID/主键无关。

        编辑:如果您通过电子邮件发送此类信息,请使用某种编码或加密,以便随机用户无法猜测其他客户的信息。密钥的来源是什么并不重要,只要它是唯一的。我的第一个建议只是在与客户打交道的系统中使用的一般建议。

        【讨论】:

        • 看,这就是我想要的。但是,如果有人对此提出质疑并说“为什么?只需使用该死的 ID”——我会拿什么回来?
        • 你为什么不使用该死的 ID?
        • 几个月前这里有一个问题,有人使用 ID 实现了客户编号,然后业务决定客户编号应该是字符串。你可以看到我要去哪里......(我真的为那个人感到难过。)
        • ... 这就是为什么 db 中的每个字段都应该是 varchar 的原因。你知道,以防万一。我开玩笑,我开玩笑!
        • @Duncan 你反驳说需要保护客户信息。但是,如果它真的很重要,则需要通过身份验证系统进行保护,而不仅仅是混淆的 customerID。但是,如果它是销售电子邮件爆炸,如果他们必须单击链接并登录以查看页面而不是仅单击链接,那么您将降低转化率
        【解决方案8】:

        向公众公开纯 ID 不是一个好主意。我解决它的方法是加密查询字符串参数。

        【讨论】:

          【解决方案9】:

          我将从这个开始。使用代理键作为返回某个数据库对象的链接是非常常见的。事实上,许多流行的框架都是开箱即用的(ASP MVC、Ruby on Rails、CodeIgniter)。

          但是,您可能会考虑使用一些自然键。您还可以尝试对 ID 字段进行 Base64 编码。即使是这样的小措施也有助于防止有人“探索”您的数据库。

          【讨论】:

          • 我不确定 ASP.NET MVC 在哪里使用代理键链接回数据库对象,你能举个例子吗?不是迂腐,我目前正在将 MVC 用于一个项目,并且仍在探索该框架。
          【解决方案10】:

          您可以在不要求用户登录的情况下实施安全机制。您不应该仅仅依靠默默无闻来保护这些数据。

          要求 URL 中的 ID 以及某种密码(随机分配的哈希值)可能是所需的全部安全性。

          【讨论】:

            【解决方案11】:

            需要考虑的是在数据库中有一个主键和一个自然键。主键唯一标识表中的一行,自然键唯一标识一个客户。如果您决定保留客户历史记录,您可以使用新的主键添加新行,但客户的自然键相同 - 然后您在单独的列中标记行“过期”或“当前”。这在客户维度的数据仓库中经常使用。在这种情况下,您将只公开自然键,而不是主键。连接仍然发生在主键上。

            【讨论】:

              【解决方案12】:

              反对使用主键的有力论据是它很容易被破解,好吧,甚至几乎没有破解 - 只是通过稍微改变数字来猜测输入的问题。如果用户尝试在其他地方手动输入链接,您甚至会无意中将自己暴露在错误类型中 - 比如说,如果他们打印电子邮件给他们的老板,然后他们输入客户 B 而不是 A 的代码。

              仅此一项就足够了。至少我会在您发送给用户的代码的末尾添加一个校验位 - 通常的递归添加数字算法很便宜并且有帮助(一点点)。

              但是我自己会错误地使用生成的 GUID。多亏了 Microsoft 和许多其他公司,现在输入 36 个字符的字符串的概念不再像以前那样冒犯用户,大多数人无论如何都会点击链接,而您有一个简单的手头。保证唯一性的功能,因此您将来永远不会绊倒自己。简单而强大。

              【讨论】:

                【解决方案13】:

                是的,使用用户名之类的东西而不是外部链接的实际主键更纯粹/更干净/更好。但是,如果没有要使用的“自然”标识符,则使用主键。只要您使用它来识别一行,而不是在其中添加一些语义值,就没有问题。

                【讨论】:

                  【解决方案14】:

                  暴露 PK 存在安全风险。用户可以进行钓鱼探险,只需尝试一些有趣的键,看看他们可以通过这种方式看到什么。假设您发送的链接是 http://www.example.org/ShowCustomer?id=4711 。通过查看此报告,用户只能找到有关客户 #4711 的信息。竞争对手可以轻松地尝试从 1 开始的所有客户 ID:http://www.example.org/ShowCustomer?id=1,从而获得所有客户的列表。那么id=0呢?还是 id=-1?或者像http://www.example.org/ShowCustomer?id=1;DROP%20TABLE%20CUSTOMERS这样的SQL注入?

                  【讨论】:

                  • 如果这些注射是可能的,那么你遇到的问题比 PK 大得多。
                  • 数据库主键并不总是一个序列号(选择不当的替代标识符可能是序列号)。
                  猜你喜欢
                  • 2011-09-13
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-11-04
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多