如何使用 OAuth2 身份验证保护私有数据?

Posted

技术标签:

【中文标题】如何使用 OAuth2 身份验证保护私有数据?【英文标题】:How can private data be secured with OAuth2 authentication? 【发布时间】:2014-11-07 13:18:23 【问题描述】:

我正在建立一个网站以使用 Google 的 OAuth2 界面进行用户身份验证。该网站将存储与每个用户相关的私人数据——我打算对其进行加密。

如果我为网站实施了自己的身份验证方法,我可以轻松地从用户的凭据(包括用户的密码)中派生出一个密钥,从而为每个用户的数据提供强有力的保护。但是使用 OAuth2,我相信我只能收到一个访问令牌,在一段时间内授予该用户权限 - 问题是访问令牌值会随着时间而变化。

OAuth2 有没有一种方法可以为我提供与用户相关的不可变机密,我可以使用该机密从中派生安全密钥?还是有其他使用 OAuth2 创建安全持久密钥的方法?

--- 编辑---

针对问题和 cmets,以下是一些需要考虑的想法:

应始终使用强加密和用户身份验证来保护所有用户信息 - 我们阅读如此多有关网站和数据库黑客攻击的新闻文章的原因是因为开发人员说“我们真的需要保护它吗”然后回答“不 -因为除了我们之外没有人可以访问数据库,安全性很困难等等”。黑客下载了数据库et violá。信用卡、电子邮件地址、电话号码、密码,随便你说出来,然后就被泄露了。 只有两个真正的秘密 - 一个是存储在某人头脑中的密码,另一个是只有授权用户才能访问的强随机值(如物理令牌)。如果您认为安全密钥可以仅从电子邮件地址派生,或者需要将机密存储在数据库中,那么您并不真正了解安全性。

我想我试图发现的是 OAuth 提供者是否可以向 OAuth 客户端提供安全链接到用户和客户端的不可变值 - 实际上,这将是一个只能由 OAuth 提供者使用解锁的密钥用户密码(他们的身份验证密码)和客户端密码(在 OAuth 协议中使用)的组合。然后,客户端可以使用此值为用户数据提供合理的安全级别。

当然,这种实施并非完美无缺,但实施得当,可以提供一种合理的方式来保护数据,同时仍然使用 OAuth 方案的良好做法。

【问题讨论】:

我投票决定关闭,因为它太宽泛了。但是,我认为这个问题很好,而且问得很好。我从不太确定这样的问题是否应该关闭。这个问题显然是从程序员的角度来看的,但它是***范围内的编程问题吗? 您打算如何从非机密信息(例如用户 ID)中导出机密信息?请记住,密码和电子邮件地址可以并且将会改变,唯一不变的是用户 ID,这不是秘密。您需要生成一个随机密钥,并将其存储在您的数据库中,与用户 ID 相关联。但是用于解密数据库中数据的秘密也在数据库中,这使得它毫无意义。 我认为不可能得到你想要的——如果你需要访问数据,那么你需要访问密钥。 “用户的秘密”和“客户的秘密”并不是真正不同的东西。 OAuth 用于确定用户是他们所说的那个人——你要么必须信任,要么不信任。 您可以做的另一件事是在应用服务器上存储一个“服务器加密密钥”,并将其与用户的密钥结合起来加密数据库中的数据。这样,如果您有数据库泄漏,假设他们没有获得您的服务器密钥,您的数据库内容仍然是安全的。 @adelphus 你解决过这个问题吗?我正在尝试做类似的事情。 【参考方案1】:

令牌的意义在于您可以使用令牌从 Google 获取有关用户的信息。在初始身份验证期间,您将告诉用户和谷歌您想要访问有关用户的某些信息:

https://developers.google.com/+/api/oauth

假设用户允许您访问他们的信息,例如他们的电子邮件地址,那么您就可以从 google 获取他们的电子邮件地址。获得他们的电子邮件地址后,您可以为他们的用户生成一个密钥,将其存储在您的用户表中,并使用它来加密他们的数据。然后,当他们再次登录时,您可以查找他们的电子邮件地址并找到他们的密钥。

对于不可变信息是否特别需要“保密”?还是只是识别用户的钥匙?

如果您要存储的信息是真正的私密信息,并且您希望将其设置为无法访问用户的数据,那么您所要做的就是为您的用户存储加密的 blob。一旦用户下载了他们的数据,他们就可以使用他们的密钥在客户端解密数据。

【讨论】:

但是用于解密数据库中数据的秘密也在数据库中。这不是让加密毫无意义吗? 啊,所以你想让你的信息也得到保护吗?在这种情况下,您根本不应该拥有钥匙。您需要做的就是为用户存储数据。密钥必须存储/派生客户端,客户端从您那里获得数据后可以解密数据。 更新了我的答案以解决这个问题【参考方案2】:

我的第一个问题是:为什么要从某些令牌中派生加密密钥?

令牌和您的加密密钥可以保持独立,并且可以与由唯一 ID 标识的用户相关联。用户身份验证可以通过您需要的任何方式通过凭据或开放 ID 身份验证或其他方式完成。但是,一旦用户通过身份验证,您的解密 API 就可以获取与经过身份验证的用户关联的解密密钥,并执行它必须执行的任何解密操作。

通过这种方式,您可以潜在地允许用户将多个开放 ID 帐户与同一个用户绑定,类似于 *** 的做法。我可以将我的 yahoo、facebook 和 google 帐户与我的 *** 用户相关联,并且可以使用这些提供商中的任何一个登录。我可以随时取消关联这些帐户。但这不会影响我的 *** 个人资料和数据。

因此,从非恒定且不断变化的事物中派生密钥并不是一个好主意。相反,请将它们分开。

【讨论】:

感谢您的回答。我理解您关于将数据加密密钥与身份验证密钥分开的观点,但良好的安全性依赖于仅从受信任的身份验证密钥中解锁的数据加密密钥 - 关键是如何做到这一点。 OpenID 似乎没有提供任何安全链接到用户的东西,客户端可以用来解锁加密密钥。如果不使用只有经过身份验证的用户(或像 OAuth 这样的受信任的提供者)知道的值,“解密 API 可以获取与身份验证用户关联的密钥”没有安全性。 我明白你的意思。我会进一步建议作为我早期解决方案的扩展,即使用对称密钥加密用户数据,并使用基于密码的加密算法使用 oauth/refresh 令牌作为密码来保护对称密钥。这样,只有令牌的持有者才能解密将解密用户数据的对称密钥。但是,您需要使用新的 oauth 令牌重新加密对称密钥。加密/重新加密对称密钥比整体数据简单高效。【参考方案3】:

如果我为网站实现了自己的身份验证方法,我可以轻松地从用户的凭据中获取密钥

此架构有一个可怕的弱点 - 如果用户忘记/重置其凭据,则密钥将永远丢失

OAuth2 有没有一种方法可以为我提供与用户相关的不可变机密,我可以使用该机密从中派生安全密钥?

OAuth2 是一种授权协议。它无意向您提供任何用户机密。

Google 的 OAuth2 应该提供一个 user info service 返回用户名(电子邮件)和一些 ID(子)。

但是,这些是身份信息,而不是任何秘密。并且混合用户凭据是恕我直言的坏主意(如前所述),使用外部 IdP(谷歌)您将无法访问凭据。

现在呢?

我的建议:

如果您确实想使用用户提供的密钥来加密用户的数据,请让用户提供密钥或由用户自己(或使用用户的公钥?)加密数据加密密钥。用户必须知道,如果这个秘密丢失,数据将无法访问。从长远来看,它是安静的不舒服。一些 DMS 系统使用这种方法来加密存储的敏感文档。

如果您想加密静态数据(在服务器、数据库上),您可能有一个特定于应用程序的密钥,恕我直言,最好存储在其他地方(密钥库、密钥管理服务......)。确实存在不同的风险概况(您必须保护密钥,..),但对用户来说更方便。这通常与其他安全措施相辅相成(即使是大企业也不会要求单独的密码来加密您的信用卡号或电子邮件)

【讨论】:

【参考方案4】:

您需要的是每个用户的恒定安全(随机)密钥,您可以从提供 OAuth2 端点的身份验证服务(在本例中为 Google)获得。

OAuth2 协议本身不提供此类值 - 身份验证服务器使用生成的非恒定值。但 OAuth2 并不禁止从资源服务器(连同用户 ID、电子邮件等)提供此值。所以基本上 OAuth2 可以让您以您想要的方式保护数据,但是您目前使用的 Google 并没有提供这种类型的恒定随机值。

另请注意,如果您让用户关联少数帐户(如 Google 和 Facebook),这将不起作用,因为它们会提供不同的随机密钥。

如果您从凭据中获取秘密,这也意味着重置密码将重置用户帐户。

此外,如果您以这种方式加密电子邮件等数据,则在当前没有登录用户的情况下无法解密它们。因此,通过电子邮件发送时事通讯几乎是不可能的。您也不能在 SQL 中查询数据。

我只能提出一些对策:

根本不存储敏感数据,或将其存储为散列。密码必须经过哈希处理,而不是加密。不要存储 CC 编号,存储代表它们的令牌。 使用密钥加密,存储在另一个数据源中。这至少增加了一些安全性 - 攻击者不仅必须获得数据库副本,还必须获得加密密钥。 由于数据已加密,因此不再需要将其存储在数据库中。您可以将加密数据存储在文件或其他来源中,这样比在数据库中更安全(没有 SQL 注入等风险)

【讨论】:

【参考方案5】:

我也遇到了同样的问题。到目前为止,我找不到安全的解决方法。

基本上,我们需要每个站点随机生成的秘密,仅提供隐式流,可用于派生凭据以访问系统和解密数据。

因为我想保护自己的数据,我可以通过两种方式编写客户端来对密钥进行加盐/哈希处理,一种是检索数据,另一种是解密。

唉,事实并非如此。

我可以从 oAuth 的基本范围内的事物中获取凭据,这将保护数据免受 me 的侵害,但这会使用户容易受到跨站点漏洞的影响,此外,还可以识别个人身份信息是一个糟糕的秘密。

我得到的最好方法是使用隐式流 oAuth2 获取用户的电子邮件地址,随机生成客户端密钥,并强制用户通过电子邮件将密钥发送给自己(作为恢复密钥),然后将密钥存储在 localStorage 中。对 secret + oauth 范围变量加盐/哈希,以派生访问、加密和解密数据所需的凭据客户端(因此用户必须登录)。

如果用户清除了他们的 localStorage,他们需要点击恢复电子邮件中的链接,这会将秘密放回 localStorage。

这将漏洞的范围放回客户端,但对公共机器有抵抗力(必须知道谁最后登录,并获得对 localStorage 令牌的访问权限),允许恢复,并且弱要求用户已登录。仍然容易受到插件注入攻击和物理访问 + 了解用户。

更新:我决定使用一些 oAuth 扩展(hello.js、文件夹 API)将用户帐户中的密钥作为文件存储。它需要一些权限和一些 API 才能实现,但似乎是可行的。

【讨论】:

这主要是一个问题,装扮成一个答案。请发布您自己的问题(并删除此问题),或编辑此问题,直到它回答问题为止。删除除最后一段之外的所有内容并对此进行扩展可能是一个解决方案。

以上是关于如何使用 OAuth2 身份验证保护私有数据?的主要内容,如果未能解决你的问题,请参考以下文章

使用 oauth2 和 jwt 在 FastApi 中进行身份验证

如何在 Azure 逻辑应用中使用 OAuth 2.0 身份验证?

具有公共/私有访问权限的 Appsync 身份验证,无需 AWS Cognito

如何使用数据库中的数据丰富 Oauth2 身份验证?

如何使用 oAuth2 对 SPA 用户进行身份验证?

使用 Azure AD 通过 OAuth2 对 Azure API 管理进行身份验证