Node.js和Socket.io:自签名证书用于安全的websocket连接

7
我一直在网上寻找一个简单明了的答案,但大多数解决方案涉及使用Express来为安全连接提供HTTP内容。我更感兴趣的是Node.js和socket.io的安全Web Socket连接(wss)。
我不使用Node.js进行HTTP请求。我使用与Node.js配合工作以实时向我的应用程序传递消息的socket.io模块。我只使用node进行Web Socket连接。
我将简要说明我的设置。我使用Django作为我的HTTP后端。用户向Django发出请求,Django将该请求的内容转发到Redis,Node.js侦听Redis的一个频道,处理内容并将消息发送给适当的接收者。
非常简单和直接。一切都很好地运行。但我担心Node.js的Web Socket连接不安全。当Node.js向收件人发送消息时,我不希望有人在中间窥探和截取消息。我想确保我的用户感到安全并信任我为他们构建的服务。
我研究了自签名证书和CA证书。两者提供相同级别的安全性。由于我仅使用Node.js进行socket.io而不提供HTTP内容,自签名证书将完全可以胜任(我构建的服务面向移动设备,而不是浏览器!)。
下面是我的socket.io实现:
var io = require('socket.io').listen(8000);
var redis = require('socket.io/node_modules/redis')
var redisChannelConnection = redis.createClient(6000, "12.345.678.9");
var redisServer = redis.createClient(6000, "23.456.789.1");

// Subscribe to Redis Channel
redisChannelConnection.subscribe('messages');

io.sockets.on('connection', function (socket) {
     socket.emit('message', 'Hello World');
     }

我目前只写了一个简单的连接函数,它可以作为普通 WebSocket 连接使用。但我想将它变成安全的 WebSocket 连接。我该如何做?

谢谢你的帮助。

3个回答

7

首先,您需要在Node中创建一个HTTPS服务器(需要使用证书):

http://nodejs.org/api/https.html
如何在Node.js中创建HTTPS服务器?

然后,您应该使用该服务器来启动Socket.io。

var io = require('socket.io').listen(myHTTPSserver);

基本上就是这些了。

有两种方法可以保护你的连接免受中间人攻击:

  • 使用签名证书,并让客户端检查该签名。互联网上有很多解释为什么这实际上是一个相当糟糕的解决方案。
  • 确保客户端只连接你的证书。任何体面的SSL/TLS库都允许你指定必须用于出站连接的证书,如果服务器端的密钥与该证书不匹配,则连接会被终止。这样做可以实现签名系统应该实现的所有功能,但不依赖于世界上每个CA证书都诚实,或者CA系统的任何其他缺点。

你的Django/Node.js组合听起来相当奇怪,理解正确吗?客户端在一个通道上发出请求,在另一个通道上接收响应?

虽然从技术上讲可能没问题,但听起来像是制造奇怪漏洞的食谱。如果必须同时使用两者,请考虑将Node作为Django内容的代理,并让Node处理所有身份验证。

无论如何,我非常怀疑仅加密两个通道中的一个就足够了,一旦黑客掌握了一个通道,很可能会有大量的升级选项。


1
可以在此处组合多个内容服务器(Node.js用于io和Django、Rails、ASP...)。 有点棘手,但通过保持它们分离,可以在使用其他技术构建的已经工作的Web应用程序上部署socket.io。 主要挑战是:(1)socket.io服务器的冗余(以便能够处理通信故障或实时部署) (2)现有Web服务器与socket.io之间的通信;管道、REST接口、protobuf,甚至通过DB或文件进行通信都是一种选择。请明智地选择,因为这可能是导致项目失败的部分。 - Txangel

2
我研究了自签名证书和由CA颁发的证书,两者提供相同级别的安全性。
不,它们并不一样。SSL的安全性与您是否使用HTTPS(例如,在SSL内部的HTTP)或wss(例如,在HTTP隧道内的套接字类型内部)无关。SSL提供端到端加密,但只有在您能够识别另一端时才能保证这种端到端加密。这就是证书的作用。由可信CA签名的证书意味着某些CA查看了证书数据并确保证书中的数据与所有者匹配。但是,自签名证书仅表示所有者本身认为一切正常,但没有人信任并查看过它。这就是为什么默认情况下不信任自签名证书,并且每个用户都必须明确信任证书(希望他已经以某种方式验证了所有者)。

1
这是我的来源 http://webdesign.about.com/od/ssl/a/signed_v_selfsi.htm,也许我没有表达清楚,但我的应用程序不是针对网页的。我专注于移动端,更具体地说是我的iOS应用程序和后端。 - deadlock
两种证书都会生成一个无法被第三方读取的网站。通过https连接或SSL发送的数据将被加密,无论证书是签名还是自签名。加密是我关心的唯一问题。由于所有请求都来自我的iOS应用程序,因此用户不会暴露出它是自签名证书还是由CA验证的。 - deadlock
1
在我看来,该源代码是错误的。如果您使用密钥交换(例如SSL)进行加密而不是使用预共享密钥,则需要识别其他站点,否则您可能会与中间人进行通信而不会注意到。因此,证书不仅是为了向客户提供一些信息,而且是为了确保您与预期的对方进行通信并获得端到端加密,而不是端到Mallory再到端的加密。您的应用程序中自签名证书的唯一可用方式是将其指纹硬编码到您的应用程序中。 - Steffen Ullrich
我明白你的观点,谢谢。今天我学到了一些新东西。 - deadlock

1

首先,我完全同意其他答案的观点,自签名证书并不像那么安全,但稍后将更多地讨论这个问题...你需要做的是要求使用“https”而不是“http”,并且在createServer中传递您的证书选项来创建您的HTTPS应用程序。这些选项与此处的TLS选项几乎相同。然后当您调用listen以进行socket.io时,您将通过HTTPS而不是HTTP进行监听。

现在回到实际问题,即对CA目的的误解。我同意,即使是自签名证书也会加密内容,但是未经任何其他人验证的自签名证书意味着任何人都可以生成他们自己的自签名证书,这个证书也是有效的。一个恶意用户在咖啡店里可以设置一个看似合法的WiFi网络。在控制该网络的情况下,他可以将自己置于所有请求的中间。他所要做的就是为您的域名生成他自己的自签名证书,然后他不仅可以解密,而且还可以修改在传输中的所有请求。这不仅适用于浏览器,还适用于Android、iPhone或其他任何移动应用程序。证书颁发机构的作用是为操作系统规定哪些根证书被信任以发放证书。因为恶意用户无法在值得信任的证书颁发机构注册证书来代表您的域名,所以通过这种方式验证的任何证书都可以保证另一端的计算机确实是预期的计算机。自签名证书在开发中很好用,但在生产中使用它们会将通过您的应用程序发送的任何数据置于欺骗您的服务器、信息泄露和消息篡改的风险之中。

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