这种无密码身份验证流程是否安全?

信息安全 验证 jwt 节点.js
2021-08-21 20:29:17

我想为我的移动应用程序实现一个无密码身份验证流程,该流程只需要用户单击其电子邮件中的链接即可登录。类似于 Slack 处理身份验证的方式。我将使用nodeandjwt来实现这个实现。

我想我已经想出了一个安全的设计,但我确定我错过了一些东西。我希望得到社区的一些批评。

开始了:

  • 用户打开移动应用程序。
  • 我们检查用户的本地存储中是否有令牌。
  • 如果他们这样做,我们将该令牌添加到他们的标题中并发送到应用程序的主页。
  • 否则,我们会提示他们输入电子邮件以开始使用
  • 当他们单击“提交”时,我们将其发布email address到我们服务器上的requestMagicLink端点。
  • 服务器检查数据库中的用户 email address
  • 如果我们找到使用该电子邮件的用户,我们会id从该用户那里获取
  • 如果用户不存在,我们创建一个新用户,然后获取 id
  • 我们使用 JWT 生成带有 的令牌id,并且我们的令牌在secret之后过期1 hour
  • 我们通过电子邮件中的链接将该令牌发送给用户。
  • 单击后,该链接将使用查询参数向magicLogin端点的服务器发送 GET 请求token
  • 我们使用 JWT 和我们的secret.
  • 如果验证失败,我们会将用户重定向到我们用电子邮件提示他们开始使用的屏幕。
  • 如果成功,我们使用 theirid和我们的secretthat生成一个新的 JWT 令牌doesn't have an expiration,然后在 URL 的参数中将其传递回用户,将用户重定向到我们应用程序中的成功页面。
  • 该应用程序从参数中获取令牌并将其存储在本地存储中,直到用户选择注销,并且用户被重定向到主页。
  • 对 api 的请求现在都在标头中包含令牌,用户可以开始了。

编辑(添加额外的想法):我正在考虑使其更安全的事情:如果我们将令牌分成两部分,将一半发送到移动设备,另一半发送到电子邮件会怎样。这样,只有有权访问该电子邮件和访问该特定设备的用户才能进行身份验证。

3个回答

您的流程(魔术链接)的主要概念是更知名的无密码身份验证替代方案之一。普遍的共识似乎是此类方案的安全性(如评论中所述)并不比允许“忘记密码”电子邮件重置机制的系统差。考虑到故障点现在在电子邮件部分,这确实是有道理的。

电子邮件服务器通常未加密,邮件最终可能存储在不安全的位置,例如用户的邮件客户端。您也无法控制用户设置其电子邮件帐户的安全程度。记住安全地实施密码重置是多么困难也可能很有用(因为它经常被拿来比较) 。最后,这是否应该让您担心实际上取决于您的威胁模型和应用程序数据的价值。

我能找到的支持魔术链接和其他无密码方案的来源主要来自那些对推动他们自己的系统实现有既得利益的供应商(例如Auth0okta),并且有一些大牌已经支持它,例如亚马逊认知。您还可以查看此堆栈中的类似问题:

Web 应用程序中的无密码身份验证 - 它有多安全?

通过电子邮件进行无密码登录 - 安全注意事项

至于您的自定义实现,有几个值得一提的问题:

  • 只要没有现有的电子邮件地址,就不要创建用户。端点和 Web 表单经常被发送垃圾邮件,您会发现您的数据库充满了垃圾。
  • 可能不需要在电子邮件中发送令牌,而是直接发送链接。只需确保链接 URL 是随机生成的,并且再次将过期设置为禁用使用。
  • 如上所述,电子邮件很容易受到威胁,因此您应该设置较短的到期时间以减轻影响。在大多数情况下,您的用户无论如何都会直接访问他们的电子邮件,因此我会将其保持在 5-10 分钟的范围内。
  • 登录后,JWT 也应该有一个过期时间,即使它很长。您真的不希望令牌无限期地有效。

老实说,如果可以避免的话,我不建议您自己设置它;尝试查找已经支持此功能的库。

所以你要做的是实现一个新的“安全协议”。我对此有一种不好的感觉,因为我认为这需要更多的研究和 QA,而不是 stackexchange 上的问题。新协议 (Auth2) 或算法 (AES) 在通常被视为“已批准”或“安全”之前需要花费大量时间和精力进行研究。

一般来说,我认为邮件不是共享访问令牌的最佳方式。你说,你创建了一个带有秘密的令牌,将带有这个令牌的链接作为查询参数发送到一个电子邮件地址,当用户点击这个链接时,他被认为是经过身份验证的。您是否还会向用户发送明文密码,然后通过网络上的查询参数输入此密码?我希望你不会因为那是非常糟糕的做法,但如果我理解你是正确的,你想要做到这一点:发送“票证”,它提供用户通过邮件以明文形式访问你的应用程序。即使它是加密的,那也不是一个好习惯。

此外,如果您收到带有未知邮件地址的 requestMagicLink-endpoit 的 POST,则不应自动“创建”新用户:这使攻击者有机会使用来自不想要的用户的邮件地址向您的数据库发送垃圾邮件他们在您的应用程序中的邮件地址;-)

我不由自主地想到的其他一些陷阱:

  • 发送邮件总是很棘手 - 黑客可以向您的用户发送假邮件或捕获和阅读发送的邮件,因为邮件没有加密
  • 重定向总是很棘手 - 黑客可以破坏一个实施不善的系统,将您的用户重定向到他们控制下的受损网站
  • 尽可能缩短进程的生命周期——如果用户在 10 分钟后没有点击邮件中的链接,则拒绝它
  • 永远不要通过查询参数发送私有数据,无论是在 GET 请求中还是在其他所有方法中
  • 当一个令牌可以被多次使用时,重放攻击的可能性

我建议您使用已使用和验证多年的最佳实践协议和方法进行身份验证,而不是编写自己的机制来阻止您的用户输入密码。否则,如果你真的想走这条路,你应该与专业人士合作,花费大量的时间和精力来确保你的机制的安全性。

不要将访问令牌/秘密放在本地存储中

通过将访问令牌放在本地存储中,它将可以通过 JavaScript 访问。这意味着令牌可以通过XSS跨站点脚本窃取此外,只有启用 JavaScript 时,身份验证才会起作用。

恶意浏览器扩展可以提取访问令牌,并将其发送给攻击者。如您所说,如果访问令牌未过期,这一点尤其重要。

为了防止 XSS 的风险,最好使用带有HttpOnly标志集的此类令牌的 cookie (您还需要使用该Secure标志,并强制HTTPS所有连接)

注意:当使用 cookie 而不是本地存储时,您必须考虑并保护自己免受CSRF跨站点请求伪造)攻击。


有用的资源: