通过用户身份验证(Node/Vue/Passport)防止中间人攻击

Posted

技术标签:

【中文标题】通过用户身份验证(Node/Vue/Passport)防止中间人攻击【英文标题】:Preventing man-in-the-middle attacks with user authentication (Node/Vue/Passport) 【发布时间】:2019-06-20 02:13:13 【问题描述】:

我目前有一个使用 Passport 处理身份验证的 Node/Vuejs 编写的 web 应用程序,但遇到了问题。我正在考虑如何设置当前的身份验证,我意识到我有一个明显的安全漏洞。

简而言之,我的 Vuex 商店访问了本地 API 端点 /api/me。该端点会简单地返回req.user。为简洁起见,典型的响应如下所示:


  username: 'Bob',
  roles: []  // normal user has no roles,
  email: 'someguy@bob.com'

我的管理路由 /admin 有一个 beforeEnter 检查,如下所示,它使用 Vuex 存储合并了这个检查,所以我可以在前端访问用户数据的缓存版本。


      path: '/admin',
      name: '/admin',
      component: Admin,
      beforeEnter: (to, from, next) => 
        store.dispatch('getMe').then(() => 
          if (store.getters.user.roles && store.getters.user.roles.includes('administrator')) 
            next();
            return;
          
          next( path: '/' );
        );
      
    

事情是这样的——我意识到有人可以轻松地玩弄这个系统。事实上,我自己用一个测试的非管理员帐户尝试过,我可以通过从 Postman 中为此目的设置的本地服务器返回以下内容来进入:


  username: 'Super Admin Joe',
  roles: ['administrator']  // normal user has no roles,
  email: 'admin@bob.com'

还有中提琴!用户现在拥有对管理页面的完全访问权限。

我的问题是,我该如何防止这种情况发生?

我需要检查用户是否在每个页面上都经过身份验证,但潜在的攻击者可以很容易地代理任何请求(在本例中为 /api/me),以使自己成为他们想要的任何用户。他们可以使用自己的帐户正常登录,打开“网络”选项卡并复制响应负载,然后根据需要更改用户数据。我相信,在检查用户的登录状态时,前端和后端之间需要进行某种加密。

我尝试考虑如何防止这种情况发生,但我端的任何东西(至少是服务器端)似乎都没有用,因为任何请求都可以很容易地重定向到攻击者的本地计算机。

关于如何“签署”我的请求以确保它们没有被代理的任何建议?提前致谢!

【问题讨论】:

为什么要进行客户端验证?为什么让客户端调用/api/me 并根据结果采取行动,而不是直接检查req.user 在管理路由中的角色? 能否直接从admin路由中的请求访问用户对象?我不认为这是可能的。 我认为更好的响应是,我有一个由 Passport 设置的 cookie,名为 connect.sid。如何在客户端使用它来检查用户的管理员状态? 【参考方案1】:

您不必对 api 请求的响应正文进行签名。进行身份验证的典型方法是建立一个签名的会话 cookie,它既可以作为外部数据库中会话信息的标识符,也可以包含会话信息本身。此 cookie 应该在您的响应的标题中,护照应该为您提供一种在您甚至没有意识到的情况下管理此 cookie 的方法。

这样,用户就不能以一种易于检测的方式篡改从服务器发送的信息,并且由于它是一个 cookie,它会随着您的浏览器的每个请求自动发送(尽管如果您使用一些AJAX 库,您可能必须明确指定要发送 cookie)。 MadEard 在评论中指的是可以使用 passprt 访问 cookie 信息的地方,passprt 是“req”对象中的“用户”属性。

【讨论】:

感谢您的回复!我想我难以理解的是,我如何在前端验证用户是他们所说的那个人? (对不起,没有完成回应)。如果有帮助,我可以向您展示我的代码。我不确定在 router.js 中做什么来锁定只到管理员的路由:github.com/gedrick/HydraStream/blob/master/src/router.jsgithub.com/gedrick/HydraStream/blob/master/server.js 还有服务器,所以你可以看到我如何序列化/反序列化用户。【参考方案2】:

阅读你的 github 文件后:

server.get("/admin", function(req, res)
    if(req.user && req.user.roles.includes("administrator"))
        //user is an administrator, render the admin panel view
    else
        //is not an admin, redirect or send error message
    
);

在每个 Express 路由中,通过 Passport 进行身份验证后,您将拥有 req.user 对象。

通过检查请求cookieconnect.sid,并检查该cookie在服务器上属于哪个会话来建立。 因此,您可以相信在任何 Express 路由中,对象 req.user 包含与该 cookie 相关的信息,您可以对其进行操作。

一点提示:随着时间的推移,进行服务器端验证应该成为您的一种反应。

客户端是用来显示信息的。在任何时候,如果您让客户做出任何可能构成安全责任的决定,请退后一步,重新考虑一下。

【讨论】:

这是非常有用的建议,谢谢! (编辑:再一次,我并不是要提交)。不过问题 - 所有视图(包括管理视图)都由 Vuejs 处理。如果他们是管理员,我是否应该让请求通过,如果不是,我应该将他们重定向到其他地方吗? 这是你可以做的,是的。重要的部分是始终检查用户是否是 服务器上的管理员,如果不是,则执行其他操作(呈现另一个视图,重定向到其他位置,发送错误消息,... )。 我见过一些人在他们的路由定义上使用 beforeEnter 来进行某种检查的例子,在我看来这就像客户端验证。当我听到您大声而清晰地“不要进行客户端验证”时,他们是如何做到这一点的?在单页应用程序上更改路由不会影响服务器,因此我无法理解如何在路由更改时验证用户状态 嗨,Geddy,我会在这个线程上回复你,因为有更多信息需要处理,我想直接处理单页应用程序评论。因此,如果您有一个单页面应用程序,并且您担心客户端更改浏览器中的数据以访问他不应该访问的页面,那么让我们评估一下场景中的危险。如果用户进入他们不应该访问的页面,是否有他们不应该访问的数据?好吧,如果后端正在安全地检查请求,那么客户端的更改无关紧要,服务器将在验证期间使请求失败 另外值得一提的是,我在前端am 为获取用户对象而进行的 ajax 调用是微不足道的,因为即使有人试图欺骗别人,任何调用回到我的服务器将首先检查其有效性。换句话说,即使 UserA 可以伪装成 UserB,一旦他们尝试添加 Twitch 关注者,我添加 Twitch 关注者的端点将首先检查他们的 cookie 是否与他们所说的相符。无论如何,非常感谢!

以上是关于通过用户身份验证(Node/Vue/Passport)防止中间人攻击的主要内容,如果未能解决你的问题,请参考以下文章

如果用户已经通过登录页面或 auth0 进行了身份验证,我们是不是需要在 websocket 连接中对用户进行身份验证?

防止 Spring Security 通过下一个身份验证提供程序对具有 BadCredentialException 的用户进行身份验证

通过表单身份验证从代码隐藏模拟用户

如何使用 Starscream 对用户进行身份验证

如果未通过身份验证,则颤振 Web 重定向用户

Laravel sanctum 检查用户是不是在没有重定向的情况下通过身份验证