为什么不公开主键


53

在我的教育中,我被告知将真实的主键(不仅是DB密钥,而且是所有主访问器)公开给用户,这是一个错误的想法。

我一直认为这是一个安全问题(因为攻击者可能会尝试读取自己的东西,而不是自己的东西)。

现在,我必须检查是否无论如何都允许用户访问,所以背后有其他原因吗?

另外,由于我的用户无论如何都必须访问数据,因此我需要在两者之间的某个外部世界拥有一个公共密钥。现在,公用密钥与主密钥存在相同的问题,不是吗?


人们一直要求有一个示例说明为什么仍然要这样做,所以这里是一个。请记住,这个问题不仅涉及原理本身,而且要在本示例中应用。明确欢迎解决其他情况的答案。

处理活动的应用程序(Web,移动设备)具有多个UI和至少一个用于系统间通信的自动API(例如,会计部门希望根据已完成的操作向客户收取多少费用)。该应用程序有多个客户,因此必须将其数据分开(从逻辑上讲,数据存储在同一数据库中)。无论如何,都会检查每个请求的有效性。

活动是非常精细的,因此它在某个容器对象中在一起,可以称之为“任务”。

三个用例:

  1. 用户A希望将用户B发送给某个任务,因此他向他发送了一个链接(HTTP)以在其中完成一些活动。
  2. 用户B必须走出建筑物,以便他在移动设备上打开任务。
  3. 会计部门希望向客户收取该任务的费用,但使用第三方会计系统,该系统通过引用REST-API的某些代码自动加载任务/活动

每个用例都要求(或变得更加容易)代理为任务和活动提供一些可寻址的标识符。


3
相关:代理密钥是否应该公开给用户?“您需要准备好暴露给需要更改的用户/客户的任何标识符,并且更改数据库中行的标识并将更改传播到所有外键只是在要求破坏数据...”
于2013年

@gnat ON UPDATE CASCADE是为此而创建的(特定于MySQL?),尽管如果问题是安全性,则访问检查应该在后端,并且无论如何都不信任用户
Izkata

2
@Izkata是的,除非您在其他数据存储中引用它们(以LDAP中的UserID作为简单示例),否则您必须从备份中恢复一些数据。gna在那里有个好处。
Angelo Fuchs

您能详细说明一下“曝光”的含义吗?一个实际的例子可能会有所帮助。:-)
CodeCaster

“暴露”是指向用户展示。(按用户,我的意思主要是人类,但该问题似乎也适用于机器)
Angelo Fuchs

Answers:


38

另外,由于我的用户无论如何都必须访问数据,因此我需要在两者之间的某个外部世界拥有一个公共密钥。

究竟。使用无状态HTTP,否则该HTTP将不知道应请求什么资源:它218306在URL中公开您问题的ID 。也许您实际上想知道暴露的标识符是否可以预测

我唯一听到否定答案的地方是这样做的:“但是他们可以更改URL中的ID!” 。因此,他们使用GUID而不是实施适当的授权。

我可以想像一种情况,您不希望标识符可预测:资源收集。如果你有一个公开托管的某些资源,其他人可能是有趣的网站,你会收留他们喜欢/images/n.jpg/videos/n.mp4其中n仅仅是一个递增的数字,任何人都在看流量,并从您的网站可以收获所有的资源。

因此,直接回答您的问题:不,直接“公开”仅对您的程序有意义的标识符也不错,通常,程序成功运行甚至需要它。


2
不可猜测的网址(例如,包含加密随机的128位令牌)是适当授权的一种形式。
CodesInChaos

对重放攻击极为敏感吗?一次使用非常好,例如密码重置URL,但对于标识静态资源而言则少得多,因为一旦公开公开令牌,任何人都可以使用它,而无需更改它就不会破坏任何合法的引用。它。
CodeCaster

嗯?显然,它需要SSL,但是无论您如何进行身份验证和授权,都是如此。通过SSL,攻击者无法学习令牌(就像他们无法学习cookie一样),并且还可以防止重播攻击。这种方法的主要缺点是您无法撤消单个用户的访问权限,因此我更喜欢仅将其用于不可变的资源。撤消对不可变资源的访问是没有意义的,因为攻击者可以简单地存储本地副本。
CodesInChaos

2
很抱歉,这些天我似乎无法表达我的意思。我的意思是,如果您希望资源是可公开访问但不可猜测的,那么为静态资源而不是增量ID使用随机令牌是可以的。对于其他任何用途,我都希望一次性使用,这是因为需要撤销。
CodeCaster

1
没有,完全是我的意思。那么,您能否详细说明“暴露”的含义?
CodeCaster

29

您不应该公开它,因为看到它的人将开始使用它作为他们的“帐号”,而不是。例如,对于我的银行帐户,我知道我的帐号是什么。我已经记住了,我在电话上与客户服务一起使用,在填写表格供其他银行进行转账,法律文件,自动转帐服务等时使用。我不想要它改变了。另一方面,我不知道或从未见过(对于我的帐户而言)主键。
多年来,存储它的系统通过银行合并,系统升级和替换等从一个系统更改为另一个系统。
主键可能会通过其中的某些转换而更改,因此,如果它从未被公开,记录或记过,任何普通用户
没有业务意义的通常称为代理键,通常(但并非总是)用作主键。

顺便说一句,当人们构建的接口和程序滥用并公开主键并使它们成为此类系统的一部分时,甚至在内部做事时,甚至会在内部发生这种情况-在内部唯一地标识数据库记录。我实际上是通过支持医院数据仓库系统的6年时间了解上述内容的。


4
+1,但实际上您是在描述替代键。并非每个表都具有代理键,即使它具有代理键也可能不是“主”键。
nvogel 2013年

2
+1我以为帐号是代理钥匙,但是我看了以后就知道100%正确:)
Michael Durrant

2
+1暴露给用户会增加隐含的要求(例如保持静态)
Matt

1
好答案。我的简短表达方式是代理键很有用,因为没有人关心它们,因此也没有人关心是否更改或不更改它们。如果您暴露它们,人们将开始关心它们。
JimmyJames

tl; dr:因为未来。如果外部需要依赖某个键,则稍后实现更改时,情况会变得混乱。因此,请将它们或多或少隐藏起来以使事情变得容易。
亚当·托利

27

因为主键是实现细节。

如果迁移数据库,则主键可能会由于插入顺序,删除旧记录等原因而发生变化,这有几个不同的原因。如果迁移数据库平台,则可能根本没有实际的主键。将PK暴露在数据访问层之上是一个泄漏的抽象,涉及所有耦合问题。


3
在没有主键的情况下,应用程序层将如何唯一地标识要从数据层检索或更新的资源?
CodeCaster

2
@CodeCaster-通过一些唯一的索引数据集,或者通过作为数据访问层提供的对象的一部分返回的非公共主键。
Telastyn

1
@CodeCaster-有很多方法可以创建令牌,该令牌允许回调指定正在执行的操作,当然不是所有的方法都直接将主键传递给了。
Telastyn

2
但这要求数据层知道哪个令牌属于(或转换为)哪个PK。对我来说,这听起来像是增加了不必要的复杂性,仅仅是为了隐藏PK。除了使建筑师满意之外,这还有什么目的?我同意您的观点,只是发现它不适用于实际用途,不胜感激。
CodeCaster

1
@CodeCaster-不,中间层实际上完成了工作,并从UI中抽象出根本没有数据访问权限。世界上有很多糟糕的建筑师,但是程序设计的许多最佳实践是有原因的。有些应用可能冒抽象泄漏的风险,而有些则不能。
Telastyn

10

这是其他答案(也就是我所学到的)的组合答案。如果您想赞成这一点,则至少应赞成其他一项,因为他们做了实际的工作。如果您更感兴趣,请阅读其他答案。

您不应公开数据库主键,而应使用代理键

  1. 如果您希望用户能够记住(至少一点点)或识别条目的标识符。(Graystone28s回答
  2. 如果您要提前计划并考虑可能会更改可能会更改PK的系统(数据库或其他)。(Telastyns回答
  3. 如果您想确保用户拥有一致的访问数据的方式,即使您的公司转移了所有权并且数据已经完全迁移到了另一个系统中,它也不会改变。(Michael Durrants回答
  4. 如果您的PK是可预测的(如序列),则您的系统可能会遇到资源获取问题。(CodeCasters回答)仅当您的系统具有值得收获的信息并且任何人或至少有收获兴趣的人可以访问的信息时,这才适用。

注意:您创建的密钥应该是人类可理解的(Sqlvogels Answer)。

如果您的系统不需要1.到4.,则没有理由不使用数据库PK作为您的公共标识符(答案中的几个)。同样,安全性不是这里的问题(有几个答案)。


8

我发现的一个原因是,在整个时间段内,我看到最终用户要求其标识符表示某种含义(例如带有前缀或接受该标识符的年份的指标)。更改PK很难,但是替代更容易。

您的主键可能是出于性能原因而希望数据库索引的内容,并且可能由于技术原因而及时将其更改,例如从数字更改为guid ...您只是不知道新技术或知识的原因是什么可能会引导您失望。您的pk是您的技术数据,公钥供最终用户使用。


7
问题是:“公开主键不好吗?” 。您的答案:“用户可能希望拥有自己的标识符”。我没有关系。我公开了InvoiceNumber,其含义对客户来说是可以更改的,但我还公开了InvoiceID,我的代码用来唯一地标识发票。您不必(通常也不想)让用户键成为存储键。这个问题是关于后者的。
CodeCaster

我认为这是一个很好的示例,因为如果您转移到APP的多租户版本,则可以保留相同的语法,并具有相同的多个发票InvoiceNumber(针对不同的租户),但是具有不同的主键-点(类型为)中也提到了答案。
2013年

1
@CodeCaster这个问题实际上是关于“为什么不希望它们相同”?
Angelo Fuchs

在这种情况下,请参见Telastyns的答案
CodeCaster

2

对于大多数应用程序,确实必须向用户公开密钥。为了有效地使用信息系统,该系统的用户通常需要一种方法来识别其中的信息,并将该信息与数据库之外的世界相关联。用关系数据库术语来说,那些标识符是键。

一种常用的设计模式是为数据库表创建一个额外的纯“技术”键,作为抽象手段。例如,提供一个稳定的(相对不变)的密钥,其中某些替代密钥可能会发生变化。此类技术密钥通常不向最终用户公开,因为这样做会破坏从用户需求中提取预期的内容。它与安全性无关。

您的问题中隐含的问题/误解是由于不恰当地使用了键一词。主键只是几个“候选”键(数据库表中的几个可能的标识符)之一。主键并不一定需要与其他任何键都有本质上不同的属性,因此专门适用于主键而不是其他键的断言和设计原则总是令人怀疑,而且常常是错误的。

鉴于您通常需要向用户公开密钥,该密钥应该是什么?尝试使您的钥匙熟悉,简单和稳定。熟悉和简单的操作使键易于阅读和记忆,并有助于避免数据输入错误。稳定性意味着不经常更改密钥,这也有助于避免错误识别的可能性。


1
这取决于...什么?我想了解通用概念背后的原因,以了解何时应用和何时不应用。
Angelo Fuchs

1
您好客户,请给我您的ID,以便我为您提供帮助。当然,其gfds789gxb3456bgfx789fgh98076hytd6734nhg5678nghf875nhgf456。嗯,你的社交怎么样?...代理ID
Michael Durrant

@Michael,答案已更新。这是一个熟悉,简单且稳定的密钥吗?
nvogel 2013年

1

这来自CodeCaster对Greystone28的回答的评论。这是您在说什么的示例:

我公开了InvoiceNumber,它具有客户的含义并且可以更改,但是我还公开了InvoiceID,我的代码使用该ID来唯一地标识发票。您不必(通常也不想)让用户键成为存储键。这个问题是关于后者的。

播放InvoiceID在您的应用程序中有什么用途?

通过暴露,我假设您的意思是用户可以看到它。仅在用户需要它才能使用您的应用程序时公开它。技术支持人员或某些管理人员都可以使用它。我已经使用了一些执行此操作的应用程序。当我知道有问题的特定记录时,确实可以更轻松地提供支持。


发票具有自然标识符(数字),但仅适用于您所写的标识符。那你得到的呢?他们有InvoiceNumber,但是它们重叠(因为两家公司使用相同的发票,并且都向您发送了发票)。在这种情况下,您的InvoiceID是唯一的,编号不是,并且使它唯一的是客户名,它不是很好的数据标识符(太长,更改频率太高,可能包含晦涩的字符...)
Angelo Fuchs

@AngeloNeuschitzer-如果用户可以通过客户名称和编号唯一标识发票,则用户不需要InvoiceID PK,但是数据库和基础代码可以使用它。它们是互斥的功能。
JeffO

请参阅我的示例的案例1-3。在任何情况下,“客户名称”都不是为用户(无论是人工还是机器)寻址该对象的有用方法。InvoiceID是PK。
Angelo Fuchs

1

实体具有暴露给外界的唯一标识符是完全正常的。对于某些对象,可能会找到实际上具有含义的标识符(例如发票编号),而对于其他对象,则不存在这样的标识符,因此必须生成该标识符。

为了保持一致性和可读性,我发现对于系统中的所有实体来说,最好使用完全相同的类型和名称作为其标识符。通常,此标识符将<type> getId()在某些抽象基类中公开()。

出于相同的原因,系统中的每个服务(例如发票服务)都应提供相同的方法来按其标识符访问实体。通常,此方法(findById(<type> id))将从通用服务接口或基类继承。

此标识符不必是实体的主键,但可以是一个。唯一需要确保的是,密钥生成策略会生成合理的唯一标识符(不一定是全球唯一的标识符,至少在系统内)。

如果以后将系统迁移(以我的经验来看是很大的话)到另一个数据库,那么使用另一种策略(不基于主键)来创建标识符就不是问题,只要该策略与原始策略兼容即可。


您能解释一下您的答案中没有其他答案的内容吗?
Angelo Fuchs

2
在我的回答中,我至少不同意摘要的第2点和第3点。我认为这些不是不使用PK作为对象标识符的正当理由。
Muton

0

主键在那里,就像您尝试作为开发人员访问的元组(记录,行)的句柄一样。它也用于参照完整性(外键约束)中,也许它也具有一个或多个用例。

从本质上讲,向用户甚至黑客公开它并没有什么不好。因为我不知道例如使用主键的攻击。

但是在安全性方面,我们有很多原则(我们接受并不同意),我们需要遵守这些原则:

  1. 租赁特权原则
  2. 默默无闻的安全

和其他一些原则。他们说的基本上是:

如果您不需要公开数据,那为什么还要呢?


句柄部分是我同意的地方。安全不是。它可能与安全性相关,但是拥有一个用户看不见的独立内部密钥实际上与安全性无关。我认为这是一个很好的副作用。
JensG

您为什么选择:请参阅我添加到问题中的示例。
Angelo Fuchs
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.