如何使用OAuth2认证保护私有数据?

17
我正在设置一个网站,使用Google的OAuth2接口进行用户认证。该网站将存储与每个用户相关的私有数据 - 我计划加密这些数据。如果我为网站实现自己的身份验证方法,我可以轻松地从用户的凭据(包括用户的密码)派生出一个密钥,从而强力保护每个用户的数据。但是对于OAuth2,我认为我只能收到访问令牌,授予该用户一段时间的权限 - 问题在于访问令牌值会随时间变化。 OAuth2是否可以提供针对用户绑定的不可变秘密,以便我可以从中派生出一个安全密钥?或者是否存在其他使用OAuth2创建安全持久秘密的方法?
回答编辑的问题和评论: 所有用户信息都应始终受到强大的加密和用户身份验证的保护。我们之所以读到那么多关于网站和数据库黑客攻击的新闻文章,是因为开发人员说“我们真的需要保护它吗”,然后回答“不需要,因为只有我们才能访问数据库,安全很难等等”。黑客下载了数据库,信用卡、电子邮件地址、电话号码、密码等等都会变得不安全。 只有两个真正的秘密 - 一个是存储在某人头脑中的密码,另一个是只有授权用户才能访问的强大随机值(例如物理令牌)。如果您认为可以仅从电子邮件地址派生出安全密钥,或者认为需要将秘密存储在数据库中,那么您并不真正了解安全性。 我想我试图发现的是OAuth提供程序是否可以向OAuth客户端提供与用户和客户端都安全关联的不可变值 - 实际上,这将是一种仅通过用户的密钥(他们的身份验证密码)和OAuth协议中使用的客户端密钥组合才能解锁的密钥。然后,客户端可以使用此值为用户的数据提供合理的安全级别。

当然,这种实现并不完美,容易被滥用,但如果正确实现,可以提供一种合理的方式来保护数据,同时仍使用OAuth方案的良好实践。


我投票关闭了这个问题,因为它太宽泛了。然而,我认为这是一个好问题,提问得很好。我从来不确定像这样的问题是否应该关闭。这个问题显然是从程序员的角度出发的,但它是否属于stackoverflow的编程范畴呢? - President James K. Polk
你究竟打算如何从非保密信息(如用户ID)中“推导”出一个秘密?请记住,密码和电子邮件地址可能会更改,唯一不变的是用户ID,而这并不是保密的。你需要生成一个随机的秘密,并将其与用户ID相关联存储在数据库中。但是,用于解密数据库中数据的秘密也在数据库中,这使得它毫无意义。 - Kris Vandermotten
我认为你所要求的是不可能实现的 - 如果你需要访问数据,那么你需要访问密钥。 "用户机密"和"客户机密"实际上并没有什么不同。 OAuth用于确定用户的身份 - 你必须相信它或不相信它。 - Daniel Scott
你可以做的另一件事是在应用服务器上存储一个“服务器加密密钥”,并将其与用户密钥结合使用以加密数据库中的数据。这样,如果数据库泄漏,假设他们没有获取到你的服务器密钥,你的数据库内容仍然是安全的。 - Daniel Scott
1
@adelphus,你解决了这个问题吗?我正在尝试做类似的事情。 - Mike Lewis
@MPLewis 不行 - 至少不是我想要的方式。最终,我选择了每个用户的密钥,并在服务启动时使用全局服务器密钥进行保护 - 这与Daniel Scott上面的建议相似。 - adelphus
5个回答

1

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

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

假设用户允许您访问其信息,例如他们的电子邮件地址,您可以从谷歌获取他们的电子邮件地址。一旦获得了他们的电子邮件地址,您可以为其用户生成一个密钥,并将其存储在用户表中,然后使用该密钥加密其数据。然后,当他们再次登录时,您可以查找他们的电子邮件地址并找到他们的密钥。
不需要将不可变信息保密,只是需要一个用于识别用户的密钥吗?
如果您要存储的信息确实是私有的,并且您希望使其无法访问您的用户数据,则只需为您的用户存储加密的 blob。一旦用户下载了他们的数据,他们可以使用他们的密钥在客户端解密数据。

6
那么用于解密数据库中数据的秘密也在数据库中,这不是使加密变得毫无意义吗? - Kris Vandermotten
啊,所以你想让信息对你也是安全的?那么,你根本不应该拥有密钥。你只需要为用户存储数据即可。秘密密钥必须在客户端存储/派生,客户端可以在从你那里获取数据后解密数据。 - Daniel Scott
更新了我的回答以解决这个问题。 - Daniel Scott

0

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

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

这样,您可以潜在地允许用户将多个开放式ID帐户与同一用户绑定,类似于Stackoverflow所做的。我可以将我的Yahoo、Facebook和Google帐户与我的Stackoverflow用户关联起来,并使用其中任何一个提供程序进行登录。我可以随时取消关联这些帐户。但这不会影响我的Stackoverflow个人资料和数据。

因此,从某些不稳定且不断变化的东西中派生密钥并不是一个好主意。相反,应该将它们分开。


谢谢你的回答。我理解你关于将数据加密密钥与认证密钥分开的观点,但是良好的安全性依赖于仅从受信任的身份验证秘密中解锁数据加密密钥 - 问题在于如何做到这一点。OpenID似乎没有提供任何与用户安全链接的东西,客户端可以使用它来解锁加密密钥。如果不使用仅经过身份验证的用户(或OAuth等受信任的提供者)知道的值,则“解密API可以获取与身份验证用户关联的密钥”中没有安全性。 - adelphus
我理解您的观点。我进一步建议作为我之前解决方案的扩展,即使用对称密钥加密用户数据并使用基于密码的加密算法来保护该对称密钥,其中oauth/refresh token作为密码。这样只有令牌持有者才能解密对称密钥,从而解密用户数据。但是,您需要使用新的oauth令牌重新加密对称密钥。加密/重新加密对称密钥比整个数据更简单高效。 - Drona

0
你需要的是每个用户都有一个常量安全(随机)密钥,你可以从认证服务中获取,该服务提供OAuth2端点(在这种情况下-谷歌)。
OAuth2协议本身不提供此值-认证服务器使用的是不恒定的生成值。但是OAuth2并不禁止从资源服务器(与用户ID、电子邮件等一起)提供此值。因此,基本上OAuth2允许您以所需的方式保护数据,但是您目前使用的Google不提供此类型的常量随机值。
还要注意,如果您允许用户关联几个帐户,例如Google和Facebook,那么这将无法工作,因为它们会提供不同的随机密钥。
如果您从凭据中派生出秘密,这也意味着重置密码将重置用户帐户。
此外,如果您以这种方式加密电子邮件等数据,则变得无法解密它们,除非当前已登录的用户。因此,发送电子邮件通讯时实际上变得不可能。您也无法在SQL中查询数据。
我只能建议一些对策:
  • 不要存储敏感数据,或者将其哈希存储。密码必须进行哈希处理,而不是加密。不要存储信用卡号码,而是存储代表它们的令牌。
  • 使用带有密钥的加密,将其存储在另一个数据源中。这样可以增加一些安全性 - 攻击者不仅需要获取数据库副本,还需要获取加密密钥。
  • 由于数据已经加密,因此将其存储在数据库中就不再必要。您可以将加密数据存储在文件或其他某个源中,在那里比在数据库中更安全(没有 SQL 注入等风险)。

-1
如果我为网站实现自己的身份验证方法,我可以轻松地从用户凭据中派生出一个密钥。但是这种方案有一个可怕的弱点——如果用户忘记/重置其凭据,则密钥将永久丢失。
OAuth2是否能够为我提供与用户相关联的不可变秘密,以便我可以从中派生出安全密钥?
OAuth2是一种授权协议。它不打算提供任何用户机密。
Google的OAuth2应该提供一个user info service返回用户名(电子邮件)和一些ID(sub)。
然而,这些是身份信息,而不是任何机密信息。混合用户凭据是不好的想法(如已经提到的),使用外部IdP(google),您将无法访问任何凭据。
那么现在怎么办?
我的建议:

如果您真的想使用用户提供的密钥加密用户数据,让用户提供密钥或者让用户自己加密数据加密密钥(或使用用户的公钥?)。用户必须意识到,如果这个密钥丢失了,数据将无法访问。从长远来看,这是非常不舒服的。一些DMS系统使用这种方法来加密存储的敏感文档。

如果您想在服务器或数据库上加密数据,可以使用应用程序特定的密钥,最好将其存储在其他地方(密钥保管库、密钥管理服务等)。确实存在不同的风险配置文件(您必须保护密钥等),但对于用户来说更加方便。通常情况下,除了其他安全措施之外,这已经足够好了(即使大型企业也不会要求单独的密码来加密您的信用卡号码或电子邮件)。


-1

我一直有这个问题。到目前为止,我找不到一个安全的解决方法。

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

为了保护数据不被我自己泄露,我可以编写客户端以两种方式对密码进行散列/加盐,一种是检索数据所需的方式,另一种是解密数据所需的方式。

但遗憾的是,情况并非如此。

我可以从oAuth的基本范围中派生凭据,这可以保护数据免受我的侵害,但这会使用户面临跨站漏洞的威胁,并且可识别个人信息不适合作为密码。

我找到的最佳解决方案是使用implicit flow oAuth2获取用户的电子邮件地址,随机生成客户端密码,并强制用户通过电子邮件将密码发送给自己(作为恢复密钥),然后将密码存储在localStorage中。对密码和oauth scope变量进行散列/加盐,以客户端方式派生凭据(因此用户必须登录)以访问、加密和解密数据。

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

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

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


这主要是一个问题,伪装成答案。请发表您自己的问题(并删除此内容),或编辑此内容直到回答问题。仅保留最后一段并扩展其内容可能是一种解决方案。 - Duncan Jones

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接