为什么要存储用户密码?

Fab*_*cke 10 security users

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

我的问题是:作为 RDBMS 用户,我为什么要为密码存储问题而烦恼,因为大多数引擎都有内置的身份验证机制。

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

CREATE USER X WITH ENCRYPTED PASSWORD Y IN GROUP baseuser;
Run Code Online (Sandbox Code Playgroud)

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

我看到了这种方法的多个优点:

  • 如果 RDBMS 决定需要更改加密算法,我不需要接触任何东西,只需应用安全更新;
  • 我很容易管理用户授权。如果用户被提升为管理员角色,我只需要将该用户添加到相应的组中即可;
  • SQL 注入现在毫无意义,因为我管理权限以允许我想允许数据库中的每个用户(例如,在像 SO 这样的论坛中,添加新帖子,回复帖子,评论和编辑/删除他自己的问题) /答案/评论);
  • “匿名”用户帐户可用于未经身份验证连接到我的应用程序;
  • 每个用户都是他提供的数据的所有者。

但在我看到的关于这个主题的几乎每个问题上,似乎都有一个普遍的共识,即这不是必须做的事情。我的问题是:为什么?

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

Bra*_*adC 27

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

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

在非常真实的意义上,这是应用程序开发人员和数据库开发人员/DBA 之间的哲学差异。

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

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

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

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

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

Dav*_*ett 8

那时分界线有点模糊,但我认为数据库级别的用户和权限是架构的一部分而不是数据的一部分,并且一般应用程序没有修改架构的地方(有,与往常一样,此规则的例外情况)。

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

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

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

  • 我确实同意,从安全的角度来看,数据库级别的用户会更好。然而,这基本上是不可能管理的,例如您可能拥有 5000 万注册用户的在线商店。每个人都需要是数据库用户。另外:这通常不适用于使用连接池的 Web 应用程序。 (3认同)

小智 6

重申问题

为什么要存储用户密码?

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

它也可能不会更好。如果我破坏了一个数据库并获得了文件的副本,我可以直接访问这些表。因此,使用出色的密码管理系统毫无意义。因为如果有人可以访问包含密码或散列密码的系统级表,他们也可以直接访问文件。既然已经有了数据,为什么还要破解密码呢?您也不能轻松加密数据。每个用户都需要能够访问它。所以用户级加密不会那么好用。

但你真正要问的是

当数据库已经有一个应用程序级别的身份验证方案时,为什么要使用应用程序级别的身份验证方案?

安全

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

在这个系统中,你的应用程序的每个用户都有一个数据库用户和密码。当然,它是一个受限用户,但它仍然是一个可以连接到数据库的用户。即使您使用行级安全性,您仍然允许最终用户知道数据库连接信息。如果曾经存在用户甚至可以获得表级访问权限的漏洞,那么您就已经打开了数据库进行攻击。并且一些漏洞已经超出了管理员访问权限。

在安全洋葱的各层中,正常的系统是:

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

在这个系统中:

  • 最终用户知道将在数据库上工作的用户/密码组合,但不可否认的是,他们的权限非常低。
  • 只需要包含一个服务器或令人信服地伪装成一个授权服务器。

我们已经失去了整个应用程序的安全级别。我们自愿放弃了部分数据库层。所以我们只需要两个漏洞:

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

如果我们能做到这两点,我们就可以访问数据库。所以我们从三层降到两层。(正常系统中的第三层是你需要一个有效的用户才能连接到数据库。)

您将对这些帐户进行大量管理。如果你犯了错误怎么办?您不是将用户 X 限制为某些行,而是授予他们对关键表的所有访问权限。那种东西通常是手动完成的,而且数量很少,所以很容易审核。但是在您的系统中,您可能有数千、数百万甚至数十亿的用户帐户,每个帐户都有自己的特殊访问权限。

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


Thi*_*rry 5

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

使用 stackexechange 继续您的示例:您如何使已删除的帖子仅对高代表用户可见?因此,对于访问规则,您仍然需要代码中的逻辑。与共享数据库规则和应用访问规则之间的访问逻辑不同,大多数开发人员更喜欢将它们都放在同一个地方。

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

使用您的解决方案,您可以省略密码列,这并不难填写:关于如何处理/散列密码有很好的库和文档。

另一点:您将如何创建连接池?我只知道 jdbc (java <-> db),你需要提供一个用户名+密码来获得连接,然后你可以池。

我想到了一些其他点,这些点比既定的方式更难/更复杂:

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

  • 连接池的实现是为了提高性能:使用它们,您不需要为每个用户请求建立连接(因此您在创建 tcp 链接时节省了 cpu/io/latency + 与数据库的所有连接和身份验证交换)。至于策略定​​义:您的数据库将在每次访问数据时不断检查访问权限,即使您已经检查过一次。此外,“拒绝或禁止访问”与“确定,没有结果”不同。不确定安全人员会更喜欢 OK 版本。 (5认同)
  • Fabian,连接池是任何规模的基础,它确实是您甚至不会考虑不使用的默认设置。以下是一些基准测试,只是为了让您了解它的重要性:使用连接池将性能提高 600 倍 (https://vladmihalcea.com/2014/04/17/the-anatomy-of-connection-pooling/) 和 * *使用连接池将性能提高 4,000 倍以上** (https://www.progress.com/tutorials/jdbc/jdbc-jdbc-connection-pooling#performance-benchmarks)。这是几个数量级的差异,它不是一个“很好的拥有”,没有它,应用程序会陷入停顿。 (3认同)