为什么要完全存储用户密码?


10

我偶尔会看到一些问题,询问如何安全地存储Web应用程序的用户密码(使用RDBMS,我不是在谈论Facebook或Twitter)。通常的答案是“先给密码加上盐,然后使用强大的算法(例如TDES或SHA512)对密码进行哈希处理”。

我的问题是:作为RDBMS用户,由于大多数引擎具有内置的身份验证机制,为什么我应该完全解决密码存储问题。

例如,如果某个用户X想要在我的Web应用程序上创建一个帐户用户密码Y,则如何发出以下查询错误:

CREATE USER X WITH ENCRYPTED PASSWORD Y IN GROUP baseuser;

然后,在我的应用程序中,用户可以使用其凭据打开与数据库的连接,而我不必操心所有密码管理。

我看到此方法有多个优点:

  • 如果RDBMS决定需要更改加密算法,那么我不需要进行任何操作,只需要应用安全更新即可。
  • 对我来说,管理用户授权很容易。如果将用户提升为管理员角色,我只需要将该用户添加到相应的组即可;
  • 现在,SQL注入已变得毫无意义,因为我可以管理权限以完全允许数据库中的每个用户(例如,在诸如SO之类的论坛中,添加新帖子,回复帖子,评论和编辑/删除自己的问题) / answers / comments);
  • 用户帐户“匿名”可用于与我的应用程序的未经身份验证的连接;
  • 每个用户都是他提供的数据的所有者。

但是,对于我在该主题上遇到的几乎每个问题,似乎都已达成共识,那就是这不是必须要做的事情。我的问题是:为什么?

注:第三点是允许的政策在PostgreSQL和安全政策在Microsoft SQL Server。我意识到这些概念是新来的,但是无论如何,既然它们已经出现,为什么我描述的技术没有成为处理用户帐户的标准方法?


2
评论不作进一步讨论;此对话已转移至聊天
保罗·怀特9

顺便说一句,通常的答案是错误的。您应该对密码加盐,然后使用慢速算法(例如bcrypt或argon2)对其进行哈希处理。
Tgr

Answers:


27

因为对于许多应用程序,他们不希望将单个用户帐户连接到数据库。用户/密码/权限/权限均在应用程序层处理,并且使用一个专用服务帐户连接到数据库后端。

作为一名DBA,我不想在数据库级别上管理某个面向公众的中型Web应用程序的10,000个活动用户,或者某个突然流行的应用程序的2+百万个用户。

实际上,这是应用程序开发人员与数据库开发人员/ DBA之间的哲学差异。

许多/大多数应用程序开发人员都不想将对应用程序功能和/或业务规则的主要方面的责任下放到数据库层。相反,他们将数据库视为简单地存储和检索数据的工具。

在某些情况下,这可能是短视的;许多RDBMS确实具有强大的功能,这些功能可以使应用程序开发人员的生活更加轻松(行级安全性,列索引,文件流存储等)。

但是其中一些更酷的功能仅在较新的版本中可用,并且组织并不总是很快升级现有环境(请参阅2014年的图表)。

在其他情况下,则首选在应用程序层处理这些事情(不只是为了数据库平台的可移植性,我坦率地说,这种说法已被夸大了)。


2
业务逻辑是应用程序中还是数据库中都存在着一场“圣战”。如果您构建了一个应用程序,其中所有业务逻辑(特别是安全性)都在数据库中(使用存储过程,视图等),那么可能会有一个论点来利用数据库安全性机制,因为没有人可以直接连接并绕过您的安全。我完全同意您应该避免在自定义表中“重建”安全机制(即登录名/密码)。当您已经具有活动目录/ O365安全性时,为什么要这样做。
Nick.McDermaid

1
@ Nick.McDermaid我想确保在数据库和应用程序之间公平地混合业务逻辑,以帮助消除潜在的未来开发人员掌握正在发生的事情的线索,这也有助于我保持工作安全,因为没人会疯狂足以管理它。
Der Kommissar

将Web服务和tomcat应用程序服务器放入其中,就可以为IBM工作
Nick.McDermaid

8

这时分界线变得有点蓬松,但是我认为数据库级别的用户和权限是模式的一部分,而不是数据的一部分,并且在一般应用程序中没有修改模式的地方(有,一如既往,此规则的例外)。

我宁愿不给应用程序他们管理架构对象所需的权限(登录,用户和权限),因为需要新用户而旧用户则要离开,因为如果该应用程序被黑客攻击,攻击者便可以访问这些权限使更容易破解数据库。在MS SQL Server中,除非您使用的是完全包含的数据库,否则登录名是服务器级别的对象,因此您需要将权限分发给单个应用程序数据库之外,这会带来更大的风险。

此外,即使您确实在数据库级别具有按应用程序用户的帐户,您仍需要应用程序级别的用户帐户来处理未经身份验证的请求-即,如果应用程序在成功通过身份验证的用户之前需要数据库的信息(也许在欢迎/登录屏幕上显示状态信息?)。

同样,如果目标是数据库引擎之间的可移植性(也许您的应用程序希望能够同时在mysql和postgres上运行?),那么您的应用程序将需要为每个引擎抽象出用户/登录管理功能,因为它们之间不是标准的-如果您打算这样做,那么您最好自己实施密码并获得所需的管理选项,而不是接受引擎提供的最低通用功能集。


1
好的,因此我认为“应用程序不应更改架构”是反对我建议的技术的第一个优点。但是,能够创建新用户的匿名帐户仅需要能够创建具有最基本访问权限的新用户。只有管​​理员可以提升用户和删除用户,我想这是应用程序所需要的:-)常规数据对匿名用户可见,因此该连接可用于不需要身份验证的内容。可移植性也是一个好点,但是我认为用户命令现在已经成为标准的一部分(不确定,不是几年前的事)
Fabian Pijcke

通过这种设置,如果黑客以匿名用户的身份访问数据库,他将能够创建所需数量的用户,但这不是安全问题,他当然也可以使用以下方法之一来膨胀数据库:创建的帐户,这将很烦人,但再没有安全性问题(无论如何,他也可以使用标准界面来完成它,尽管这样会更慢:p)
Fabian Pijcke

3
我确实同意,从安全角度出发,数据库级别的用户会更好。但是,这基本上不可能通过例如拥有5000万注册用户的网上商店来管理。每个人都需要是一个数据库用户。另外:这通常不适用于使用连接池的Web应用程序。
a_horse_with_no_name

6

重提问题

为什么要完全存储用户密码?

最简单的答案是您必须这样做。您仍然以替代方法存储密码。只是您使用数据库的内置系统来管理存储。因此,您的方法仅与数据库的方法一样好。这可能仍然比您可能会做的任何其他事情要好,但是这并不能避免存储。这实际上只是避免编码存储。

也可能不会更好。如果我破坏了数据库并获得了文件的副本,则可以直接访问表。因此,使用出色的密码管理系统毫无意义。因为如果有人可以访问包含密码或哈希密码的系统级表,那么他们也可以直接访问文件。当您已经拥有数据时,为什么还要担心破解密码?这也不像您也可以轻松地加密数据。每个用户都需要能够访问它。因此,用户级别的加密将无法很好地工作。

但是你似乎真正要问的是

当数据库已经有一个身份验证方案时,为什么要使用它呢?

安全

其他人忽略了您可能编写更安全的版本的可能性,并提出了许多原因。我普遍同意。但是还有一个没有人提到的东西。您正在损害数据库的安全性。

在此系统中,应用程序的每个用户都有一个数据库用户和密码。当然,它是受限用户,但仍然是可以连接到数据库的用户。即使您使用行级安全性,您仍然允许最终用户知道数据库连接信息。如果有人利用过漏洞甚至可以让用户获得表级访问权限,那么您就可以打开数据库进行攻击。而且,除了管理员访问之外,还有其他一些漏洞。

在安全性洋葱层中,正常系统为:

  • 数据库,仅允许来自某些计算机的连接。
  • 数据库,仅允许作为特定用户/密码组合的连接。
  • 具有数据库权限的服务器,允许来自应用程序的连接。
  • 应用程序在服务器上运行。
  • 应用程序具有有限权限的数据库连接信息。
  • 在允许用户修改数据库之前,应用程序会对用户进行身份验证(可能要创建新帐户除外)。

在此系统中:

  • 最终用户知道可以在数据库上工作的用户/密码组合,但是特权很低。
  • 只需要包含一台服务器或说服自己是授权服务器即可。

我们已经失去了整个应用程序的安全级别。并且我们自愿放弃了数据库层的一部分。因此,我们仅需要两个漏洞利用:

  1. 欺骗或破坏授权服务器。
  2. 增加受限特权帐户的访问权限。

如果我们能够做到这两件事,就可以访问数据库。因此,我们从三层下降到两层。(普通系统中的第三层是您需要有效的用户才能连接到数据库。)

您将对这些帐户进行大量管理。如果您犯错了怎么办?不仅将用户X限制为仅某些行,还授予他们所有访问关键表的权限。这类工作通常是手动完成的,只有很少一部分,因此易于审核。但是在您的系统中,您可能正在做成千上万个甚至数以十亿计的用户帐户,每个用户帐户都有自己独特的访问权限。

就此而言,数据库系统是否可以扩展到数百万或数十亿用户?如果可以,那么规则数量呢?如果每个用户都使用一百条或更多条关于他们可以访问和不能访问的规则,那么这是否可以扩展到十亿用户?您正在采用通常是小型的系统,并将其规模化。那不一定行得通。随着您的成长,您可能会发现系统限制。


5

您描述的内容将处理身份验证,而不处理授权(用户可以访问哪些数据),或者仅在最简单的用例中处理。

继续使用stackexechange进行示例:如何使已删除的帖子仅对高级用户可见?因此,对于访问规则,您仍然需要代码中的逻辑。大多数开发人员不愿在数据库规则和应用程序访问规则之间共享访问逻辑,而是更喜欢将它们全部放在同一位置。

您还需要一个表来存储用户信息:用户不仅仅是用户名+密码。他有一封电子邮件,一个声誉等等。因此,您仍然需要用户表,只要可以访问它,就必须确保它与数据库用户表保持同步。

在您的解决方案中,您只需省略一个密码栏即可,填写起来并不难:有很多关于如何处理/哈希密码的好的库和文档。

另一点:如何创建连接池?我只知道jdbc(java <-> db),并且需要提供用户名+密码才能获得连接,然后可以进行池化。

我想到的其他一些要点要比既定的方法更难/更难以实现:

  • 处理密码恢复
  • 初始实施2年后,您的产品经理会要求您添加对oauth计划的支持,或允许双因素身份验证

诸如此类的CREATE POLICY deleted_posts_high_rep ON posts FOR SELECT TO baseuser USING ((SELECT rep FROM users WHERE name = current_user()) > 10000)问题可以回答您的第一个问题。我从来没有说过我不再需要用户表,并且确保与db用户表的同步有些棘手,但有可能。讨论的技术的重点是安全性。至于连接池,我使用OCaml,我不得不说我不明白为什么我需要连接池。我目前使用哈希映射,将Cookie值与返回连接的0元函数关联:-)
Fabian Pijcke,

5
实现连接池是为了提高性能:使用它们,您不需要为每个用户请求建立连接(因此,您在创建tcp链接时节省了cpu / io / latency +与数据库的所有连接和auth交换)。至于策略定​​义:即使您已经对数据进行了一次检查,您的数据库也将不断检查对每个数据访问的访问权限。“拒绝访问或禁止访问”也与“确定,没有结果”不同。不确定安全人员会喜欢OK版本。
蒂埃里(Thierry)

3
Fabian,连接池在任何规模上都是基础,这实际上是默认设置,您甚至都不会考虑不使用它。以下是一些基准测试,旨在让您了解它的重要性:连接池将性能提高600倍vladmihalcea.com/2014/04/17/the-anatomy-of-connection-pooling),而连接池将性能提高4,000倍以上连接池progress.com/tutorials/jdbc/…)。这是几个数量级的差异,这不是一个“不错的东西”,如果没有它,应用程序将停顿下来。
伊万·麦卡

1
有趣的数字。实际上,我从未见过没有使用连接池的应用程序。但是我不知道(并期望)它们能使速度大大提高。连接池的另一个方面是资源控制:由于有了它们,客户端一次尝试全部连接的大量增加将使您的应用程序变慢,但不会对数据库造成太大的影响(如果池配置正确) 。
蒂埃里

好的,我只是搜索了有关连接池的更多信息,并且我同意,只要网站每秒必须处理一百个以上的连接(这不是那么高),这是非常有效的一点。似乎PostgreSQL中的每个连接都吃掉大约10MiB的RAM!我从来没有想过那么多。
法比安·皮克

1

另一点:许多[1] Web应用程序允许您通过应用程序本身在线注册和创建用户帐户。这意味着您的匿名用户(该用户应该拥有的最低特权)必须具有创建用户和授予特权的权限!

好的,可以限制它可以授予的特权,并为更高级别的用户提供更多的授予选项(我不知道这是-通常不是“授予特权”单个特权)。但是,这仍然意味着将具有授予特权的内容放在网上,以供所有人破解。我敢肯定,如果我那样做,我的DBA不会喜欢我的:)

我知道这个问题总会存在,但是如果您不允许Web应用程序管理数据库用户,则您有防御层,尤其是在删除数据时。

同样,这将防止任何使用池化或重用的数据库连接。

[1]我最多会说,但没有数字可以支持

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.