MEANJS:SocketIO 中的安全性

Posted

技术标签:

【中文标题】MEANJS:SocketIO 中的安全性【英文标题】:MEANJS: Security in SocketIO 【发布时间】:2015-09-23 10:35:44 【问题描述】:

情况

我在 MEAN.JS 应用程序中使用库 SocketIO。

在 NodeJS 服务器控制器中

    var socketio = req.app.get('socketio');
    socketio.sockets.emit('article.created.'+req.user._id,  data);

在 AngularJS 客户端控制器中

   //Creating listener        
   Socket.on('article.created.'+Authentication.user._id, callback);

   //Destroy Listener
   $scope.$on('$destroy',function()
        Socket.removeListener('article.created.'+Authentication.user._id, callback);
   );

好的。效果很好...

问题

如果一个人(黑客或其他人)获得了用户的id,他可以在另一个应用程序中在同一频道中创建一个监听器,他可以查看发送给用户的所有数据;例如所有的通知...

我怎样才能做同样的事情但更安全?

谢谢!

【问题讨论】:

【参考方案1】:

前段时间我偶然发现了同样的问题。这是我的解决方案(稍作修改 - 用于生产)。

我们将使用 Socket.IO namespaces 为每个用户创建私人 房间。然后我们可以将消息(服务器端)发送到特定的房间。在我们的例子中 - 只有特定的用户才能收到它们。

但是要为每个连接的用户创建私人房间,我们必须首先验证他们的身份。我们将为此使用简单的身份验证中间件,supported by Socket.IO since its 1.0 release。

1。认证中间件

自 1.0 版本以来,Socket.IO supports middleware. 我们将使用它:

    验证连接用户身份,使用JSON Web Token(见jwt-simple)他发送给我们作为查询参数。 (请注意,这只是一个示例,还有很多其他方法可以做到这一点。) 将他的用户 ID(从令牌中读取)保存在 socket.io 连接实例中,以供以后使用(在第 2 步中)。

服务器端代码示例:

var io = socketio.listen(server); // initialize the listener

io.use(function(socket, next) 
  var handshake = socket.request;
  var decoded;

  try 
    decoded = jwt.decode(handshake.query().accessToken, tokenSecret);
   catch (err) 
    console.error(err);
    next(new Error('Invalid token!'));
  
  if (decoded) 
    // everything went fine - save userId as property of given connection instance
    socket.userId = decoded.userId; // save user id we just got from the token, to be used later
    next();
   else 
    // invalid token - terminate the connection
    next(new Error('Invalid token!'));
  
);

这里是关于如何在客户端初始化连接时提供令牌的示例:

socket = io("http://***.com/", 
  query: 'accessToken=' + accessToken
);

2。命名空间

Socket.io namespaces 让我们能够为每个连接的用户创建私人房间。然后我们可以将消息发送到特定的房间(因此只有其中的用户才能接收它们,而不是每个连接的客户端)。

在上一步中,我们确保:

    只有经过身份验证的用户才能连接到我们的 Socket.IO 接口。 对于每个连接的客户端,我们将用户 ID 保存为 socket.io 连接实例 (socket.userId) 的属性。

剩下要做的就是在每次连接时加入适当的房间,名称等于新连接客户端的用户 ID。

io.on('connection', function(socket)
  socket.join(socket.userId); // "userId" saved during authentication

  // ...
);

现在,我们可以发出只有该用户会收到的有针对性的消息:

io.in(req.user._id).emit('article.created', data); // we can safely drop req.user._id from event name itself

【讨论】:

首先谢谢你,因为这是一个很棒的答案!我现在有问题......在客户端代码示例中是变量“tokenSecret”,它是什么?我需要在这个变量中放入什么?与客户端代码示例中的变量“accessToken”相同。抱歉,我以前从未使用过 JSON Web Token。再次感谢您! 顺便说一下,我在客户端使用 AngularJS。我需要使用 bower 安装任何 JWT 库以提供 accessToken?谢谢 是的,我有点担心也许我应该多解释一下这两个,但我停下来,因为这似乎有点“离题”。 tl;dr:tokenSecret 可以是任何字符串,越长越随机越好。这是服务器将用于编码和解码令牌的内容,因此永远不会与客户端共享它。 accessToken 是您必须首先从服务器获取的内容。您应该给它用户名、密码,并取回使用jtw.encode() 生成的令牌,其有效负载包含新认证用户的userId(例如 userId: ... )。 如果这还不够,我会尝试编辑答案以包括重要/缺失的一两个,希望不会进入任何真正的题外话。也许(虽然我自己不推荐它)在与socket.io 连接时传递用户名和密码而不是 JTW 令牌会更简单,然后根据用户名/密码组合加载完整的用户对象,所有这些都在身份验证中间件中我提供(仅稍作修改)? (我看到您使用的是 Mongoose/MongoDB。) 好吧,我试着理解它...我把变量 tokenSecret 像这样:var tokenSecret = '570ee3818635829e35700b0166ccfec8'; (invented) 放在 服务器端payload 是我们在client-side 中发送并在socket.request.query().accessToken 中在服务器中接收的accessToken,不是吗?这个有效载荷是编码的,对吗?如果我们永远无法与任何客户端共享 tokenSecret,我们在哪里编码有效负载?顺便说一句,存在这个错误:[TypeError: Object #<IncomingMessage> has no method 'query'] with .query() 方法。谢谢。

以上是关于MEANJS:SocketIO 中的安全性的主要内容,如果未能解决你的问题,请参考以下文章

MEANJS 样板:在哪里包含自定义 javascript 文件?

Meanjs 无法将图像保存到嵌套模式中

如何使用 Socketio-jwt node.js 对用户进行身份验证

meanjs 解决方案中是不是有令牌身份验证?

我应该选择啥:Meteor 还是 MeanJS? [关闭]

在 MeanJS 中上传图片