当前位置: 首页 > 后端技术 > Node.js

Node.js认证的教程(大概)错了

时间:2023-04-03 10:46:20 Node.js

原文地址:你的Node.js认证教程(大概)错了原作者:micaksica#大概)错了找了很多Node.js/Express的教??程.js身份验证。所有这些都是不完整的,甚至会以可能伤害新用户的方式产生安全漏洞。当其他教程不再帮助您时,您可能想看看这篇文章,它探讨了如何避免一些常见的身份验证陷阱。我也一直在寻找Node/Express中功能强大的一体化解决方案,以与Rails的设计竞争。更新(8.7):在他们的教程中,RisingStack声明他们不再以明文形式存储密码,选择在示例代码和教程中使用bcrypt。更新(8.8):编辑标题有关Node.js身份验证的教程(可能)是错误的,本文纠正了这些教程中的一些错误。在业余时间,我一直在研究各种Node.js教程,似乎每个Node.js开发人员都有一个博客,他们在上面发布自己的教程,介绍如何以正确的方式做事,或者更准确地说,他们如何做事的方式。成千上万的前端开发人员投入服务器端JS漩涡,试图通过复制粘贴或免费npm安装从这些教程中拼凑出可操作的知识,以便在外包经理或广告代理处完成工作完成开发给定的截止日期。Node.js开发中比较有问题的事情之一是身份验证过程主要是由开发人员通过实验开发的。Express.js世界中事实上的身份验证解决方案是Passport,它提供了许多身份验证策略。如果你想要一个类似于Plataformatec设计的强大的RubyonRails解决方案,你可能会对Auth0感兴趣,这是一个将身份验证作为服务的开创性项目。与Devise相比,Passport只是身份验证中间件,不处理任何其他身份验证:这意味着Node.js开发人员可以自定义自己的API令牌机制、密码重置令牌机制、用户身份验证路由、端点、多种模板语言,因此有大量教程致力于为您的Express.js应用程序设置Passport,但很少有正确的教程,没有一个正确实现了Web应用程序所需的完整堆栈。请注意:我并不是要针对这些教程的开发人员,而是要利用他们的身份验证缺陷在他们自己的身份验证系统中制造安全问题。如果您是教程作者,请在更新教程后随时与我联系。使Node/Express成为供开发人员使用的更安全的生态系统。错误一:凭证存储让我们从凭证存储开始。存储和调用凭据是身份管理的标准,而传统方法是在您自己的数据库或应用程序中存储或调用它们。Credentials作为中间件,简单的说“这个用户可以通过”或者“这个用户不能通过”,需要passport-local模块来处理自己数据库中的密码存储,这个模块也是Passport.js作者写的。在我们深入研究本教程之前,请记住OWASP的密码存储备忘单,它归结为“使用唯一的盐和单向自适应成本函数存储高熵密码”。或者先看看CodaHale的bcrypt模因,即使存在一些争议。作为Express.js和Passport的新用户,我首先要谈的是passport-local本身的示例代码。非常感谢passport官方提供了一个可以克隆扩展的示例Express.js4.0应用,方便我克隆扩展。但是,如果我只是复制示例,我不能说太多,因为没有数据库支持的示例,它假设我只是使用一些设置的帐户。没关系吧?这只是一个内部网应用程序,开发人员说下周我将再分配四个项目。当然,此示例的密码未以任何方式进行哈希处理,而是与此示例中的验证逻辑一起以明文形式存储。此时,甚至不考虑凭证存储。让我们在google上搜索另??一个关于使用passport-local的教程。我从RisingStack上一个名为“节点英雄”的系列中找到了这个快速教程,但我没有从这个教程中找到太多帮助。他们还在GitHub上提供了一个示例应用程序,但它与官方问题相同。(Ed.8/7/17:RisingStack现在在他们的教程应用程序中使用bcrypt。)接下来,这是第四个结果,来自于2015年编写的Google制作的expressjspassport-local教程。它使用MongooseODM并实际读取凭证从我的数据库。本教程相当完整,包括集成测试,是的,您可以使用另一个样板。但是,MongooseODM还存储String类型的密码,因此这些密码也以明文形式存储,只是这次存储在MongoDB实例上。(每个人都知道MongoDB实例通常非常安全)您可以指责我挑选教程,如果挑选教程意味着从Google搜索结果的第一页中挑选,那么您是对的。让我们选择TutsPlus上排名较高的passport-local教程。这个更好,因为它使用brypt的因子10密码哈希并使用process.nextTick来延迟同步bcrypt哈希检查。谷歌的最高分来自scotch.io的教程,同样使用成本系数较低的bcrypt8。两个值都很小,但8真的很小。大多数bcrypt库现在使用12。成本因子选择8是因为管理员账号是十八年前的,当时这个因子数就足够了。除了密码存储之外,这些教程都不会实现密码重置功能,这对开发人员来说是一个挑战,并且有其自身的缺陷。错误二:密码重置密码存储的一个姊妹安全问题是密码重置,并且没有顶级基础教程解释如何使用Passport进行重置。你必须找到另一种方法。有一千种方法可以搞砸。我见过人们犯的最常见的重置密码错误是:可预测的令牌。基于当前时间的令牌就是一个很好的例子。糟糕的伪随机数生成器会产生相对较好的标记。存储不良。在数据库中存储未加密的密码重置令牌意味着如果数据库遭到破坏,这些令牌就是明文密码。使用加密安全随机数生成器生成长令牌可防止对重置令牌的远程暴力攻击,但不能防止本地攻击。重置令牌是凭据,应该这样处理。没有令牌过期。没有过期时间的令牌给攻击者更多时间来利用重置窗口。没有二次数据验证。安全问题是重置的实际数据验证。当然,开发人员必须选择一个好的安全问题。安全问题有其自身的问题。虽然这对于安全性来说似乎有点矫枉过正,但电子邮件地址是您拥有的东西,而不是您知道的东西,并且会将身份验证因素混入其中。您的电子邮件地址成为每个帐户的关键,只需将重置令牌发送到电子邮件即可。如果您不熟悉这些内容,请尝试使用OWASP的密码重置工作表。让我们回到Node,看看它为此提供了什么。我们将暂时转到npm并重新访问密码重置,看看是否有人已经这样做了。有一个已有五年历史的包裹(这通常意味着它很棒)。在Node.js时间线中,这个模块就像侏罗纪时代,如果我要挑剔的话,Math.random()在V8中是可预测的,所以它不应该用于令牌生成代码中。此外,它不使用Passport,所以让我们继续。在StackOverflow上无法获得太多帮助,因为一家名为Stormpath的公司的开发人员喜欢将他们的IaaS启动教程插入到与此相关的每个可以想象的线程中。他们的文档到处都是,他们也有关于密码重置的博客广告。然而,所有这一切都随着Stormpath的关闭而停止,它于2017年8月17日完全关闭。好的,回到谷歌,这里似乎存在唯一的教程。我们找到了谷歌搜索快速护照密码重置的第一个结果。或者我们的老朋友bcrypt。文章中使用了较小的成本因子5,这比现代使用的成本因子要低得多。然而,与其他的相比,这个非常有用,因为它使用crypto.randomBytes来生成真正随机的令牌,如果你不使用它们就会过期。但是,上述做法中的#2和#4与本综合教程不符,因此加密令牌本身容易受到身份验证错误、凭证存储的影响。幸运的是,由于重置过期,这用途有限。但是,如果攻击者通过BSON注入读取数据库中的用户对象,或者由于配置错误而可以自由访问Mongo,则这些令牌非常危险。攻击者可以简单地为每个用户发出密码重置,从数据库中读取未加密的令牌,并为用户帐户设置自己的密码,而无需使用GPU装置对bcrypt哈希执行昂贵的字典攻击过程。错误#3:API令牌API令牌是凭证。它们与密码或重置令牌一样敏感。大多数开发人员都知道这一点,并试图将他们的AWS密钥、Twitter机密等保密,但这似乎并没有延续到正在编写的代码中。让我们使用JSONWebToken获取API凭据。拥有无状态、可列入黑名单、可自定义的令牌比使用了十年的旧API密钥/秘密模型要好。也许我们的一位初级Node.js开发人员听说过JWT,或者看到了passport-jwt,并决定实施JWT策略。无论如何,任何接触到JWT的人都会或多或少地受到Node.js的影响。(亲爱的ThomasPtacek会认为JWT不好,但恐怕船已经在这里航行了。)我们用google搜索了expressjsjwt并找到了SoniPandey的教程UsingJWT(JSONWebToken)inNode.jsforuserverify。不幸的是,本教程实际上并没有帮助我们,因为它不使用凭据,但是当我们使用它时,我们很快会注意到凭据存储中的一个错误:我们将以明文形式将JWT密钥存储在商店图书馆。我们将使用对称密码来存储密码。这意味着我可以获得加密密钥并在发生泄露时解密所有密码。加密密钥与JWT秘密共享。我们将使用AES-256-CTR进行密码存储。我们不应该使用AES来启动,而且这种操作模式没有多大帮助。我不知道为什么选择这种特定模式,但单一选择使密文具有可塑性。让我们回到谷歌并寻找下一个教程。Scotch,在passport-local教程中做了一个密码存储工作,比如忽略他们之前告诉你的内容并以明文形式存储密码。好吧,我们将提供有关凭据的简短教程,但这对只是复制的开发人员没有帮助。因为更有趣的是,本教程将这个mongooseUser对象序列化为JWT。让我们从Scotch克隆这个存储库并按照说明进行操作。可以忽略Mongoose的一些警告,我们可以输入http://localhost:8080/setup创建一个用户,然后使用默认凭据“NickCerminara”和“password”调用/api/authenticate获取令牌。此令牌返回并显示在Postman上。从Scotch教程返回的JWT令牌。请注意,JSONWeb令牌已签名但未加密。这意味着两个时期之间的大斑点是一个Base64编码的对象。快速解码后,我们得到了一些有趣的东西。我喜欢在明文密码中使用令牌。现在,任何东西,包括存储在Mongoose模型中的令牌,甚至过期都有您的密码。鉴于这来自HTTP,我可以将其从网络中取出。下一个教程怎么样?下一个教程Express、Passport和JSONWebTokens(jwt)forbeginners,包含相同的信息泄露漏洞。SlatePeak的下一篇教程做同样的连载文章。至此,我放弃了读书。错误#4:速率限制如上所述,我在任何这些身份验证教程中都没有发现任何速率限制或帐户锁定方面的问题。在没有速率限制的情况下,攻击者可以执行在线字典攻击,例如运行BurpIntruder等工具,以访问密码较弱的帐户。帐户锁定也可以通过要求用户在下次登录时填写扩展登录信息来帮助实现这一点。请记住,速率限制也有助于提高可用性。跨平台文件加密工具是CPU密集型功能,没有限速功能,使用跨平台文件加密工具会导致应用拒绝服务,尤其是在CPU高位运行时。用户注册或检查登录密码等多个请求,虽然是轻量级的HTTP请求,但会占用大量昂贵的服务器时间。虽然我没有教程来证明这一点,但Express有很多限速技术,例如express-rate-limit、express-limiter和express-brute。我无法评论这些模块的安全性,我什至都没有看过它们;不管你的负载平衡使用什么,通常我建议在生产中运行一个反向代理,并允许nginx对请求进行速率限制。身份验证很难我敢肯定这些有问题的教程开发人员会争辩说,“这只是为了解释基础知识!没有人会在生产中这样做!”但是,我怎么强调这是多么错误都不为过。当你教程中的代码放在这里时,人们会参考和使用你的代码,毕竟你比他们更专业。如果您是初学者,请不要相信您的教程。复制教程中的示例可能会导致您、您的公司和您的客户在Node.js世界中遇到身份验证问题。如果你真的需要一个健壮的、生产就绪的一体式身份验证库,有更好的选择,比如Rails/Devise,它更稳定,也更可靠。Node.js生态系统虽然易于访问,但仍然为JavaScript开发人员动态编写Web应用程序以进行生产部署留下许多悬而未决的问题。如果你有前端背景,不懂其他编程语言,我个人认为使用Ruby是个不错的选择,毕竟站在巨人的肩膀上比从头学这些东西容易多了.如果您是教程作者,请更新您的教程,尤其是样板代码。此代码可能会被其他人复制到生产中的Web应用程序中。如果您是顽固的Node.js用户,希望您在本文中学到了有关使用凭据进行身份验证的知识。你可能有什么问题。在这篇文章中,我还没有找到完全避免上述错误的完美方法。向您的Express应用程序添加凭证验证不应该是您的工作。应该有更好的方法。