node.js - WebSocket通过Amazon ELB或直接通过WebSocket(远程IP问题)

标签 node.js amazon-ec2 websocket amazon-elb

我们使用WebSockets与我们的EC2实例进行通信。
我们的脚本是使用nodejs和Express提供的,然后初始化WebSocket。
现在使用了ELB,这使得识别客户端IP变得更加困难。
使用x-forwarded-for标头,我们可以在HTTP上下文中获取IP,但是,当涉及到服务器中的WebSocket上下文时,它似乎没有被Amazon转发。

我们确定了2个选项:


将WebSocket与实例直接通信(使用其公共DNS)。
维护某种会话ID,在HTTP上下文中存储IP并将其与会话ID关联。客户端将使用HTTP响应获取其sessionid,并将其用于WebSockets。服务器将识别客户端并从缓存中解析其IP。


两种选择都不是很好:1不是容错的,2是复杂的。
还有更多解决方案吗?亚马逊可以以某种方式转发IP吗?最佳做法是什么?

谢谢

最佳答案

我曾经使用过websockets,也曾经使用过ELB,但是我从未与他们一起使用过,所以我没有意识到Elastic Load Balancer上的HTTP转发器无法理解websocket请求...

因此,我认为您必须使用TCP转发器,这说明了为什么您使用其他端口,并且TCP转发器当然不支持协议,因此根本不会添加任何标头。

一个似乎相当通用且简单的选项是,应用程序的HTTP端通过推送信息而不是将其存储在缓存中以进行检索来建议websocket端。假设您的环境中没有阻碍或难以实现的障碍,那么它具有可伸缩性和轻巧性。

在生成加载websocket的网页时,使用字符串“ ipv4:”和客户端的IP(例如“ 192.168.1.1”),对其进行连接和加密,并使结果对url友好:

/* pseudo-code */
base64_encode(aes_encrypt('ipv4:192.168.1.1','super_secret_key'))


使用具有128位aes的示例密钥和示例IP地址,我得到:

/* actual value returned by pseudo-code above */
1v5n2ybJBozw9Vz5HY5EDvXzEkcz2A4h1TTE2nKJMPk=


然后,在为包含websocket的页面呈现html时,动态生成url:

ws = new WebSocket('ws://example.com/sock?client=1v5n2ybJBozw9Vz5HY5EDvXzEkcz2A4h1TTE2nKJMPk=');


假设您的代码可访问websocket的querystring,则可以使用超级密钥对base64_decode进行查询,然后aes_decrypt在查询参数“ client”中找到的字符串,然后验证其以“ ipv4:”开头。它不是,那不是合法的价值。

当然,“ ipv4:”(在字符串的开头)和“客户端”(对于查询参数)是任意选择,没有任何实际意义。我对128位AES的选择也是任意的。

当然,此设置的问题在于它会受到重播:给定的客户端IP地址将始终生成相同的值。如果仅将客户端IP地址用于“信息用途”(例如,记录或调试),则可能就足够了。如果您将它用于更重要的用途,则可能需要扩展此实现-例如,通过添加时间戳:

'ipv4:192.168.1.1;valid:1356885663;' 


在接收端,解码字符串并检查时间戳。如果不是您认为安全的+/-秒间隔,则不要信任它。

这些建议全都取决于您动态生成websocket网址的能力,浏览器​​与之连接的能力以及您能够访问websocket请求中URL的querystring部分的能力……但是,如果这些内容能够做到,也许这会有所帮助。



其他想法(来自评论):

我在上面建议的时间戳是seconds from the epoch,它为您提供了一个增量计数器,该计数器在您的平台中不需要状态-仅要求您所有服务器时钟都是正确的-因此不会增加不必要的复杂性。如果解密后的值所包含的时间戳小于(例如)与服务器当前时间相差5秒(+/-),则说明您正在处理经过身份验证的客户端。允许的时间间隔只需要是客户端在加载原始页面后尝试进行websocket连接的最大合理时间,以及所有服务器时钟的最大时滞。

当然,使用NAT,多个不同的用户可能在同一个源IP地址后面。确实如此,尽管可能性要小得多,但用户实际上可以使用与发起第一个http连接的源IP不同的源IP进行websocket连接,并且仍然相当合法……这听起来像是对于您来说,经过身份验证的用户可能比实际的源IP更重要。

如果还将经过身份验证的用户ID包含在加密的字符串中,则该值对于原始IP,用户帐户和时间都是唯一的,精度为1秒。我认为这就是您所说的其他盐。将用户帐户添加到字符串中应该会为您提供所需的信息。

'ipv4:192.168.1.1;valid:1356885663;memberid:32767;' 


TLS应该阻止未经授权的一方发现此加密字符串,但是避免重播也很重要,因为在HTML页面的用户浏览器的“查看源”中,生成的URL以明文形式提供。您不希望今天获得授权但明天未经授权的用户能够使用应该被识别为不再有效的签名字符串来欺骗他们。键入时间戳并要求其落入非常小的有效窗口中可以防止这种情况。

关于node.js - WebSocket通过Amazon ELB或直接通过WebSocket(远程IP问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14038928/

相关文章:

javascript - 在 haskell websockets 示例服务器上捕获刷新?

scala - 您如何通过 Play Framework 中的 Websockets 将数据从 Kafka 流发送到客户端?

scala - Play Framework 2.2.0 [scala] - WebSocket.async 与 WebSocket.using[T]

javascript - 定位对应的未经过V8优化的JS代码源

amazon-web-services - 多个 vpc 上的 Kubernetes

amazon-web-services - 自动缩放组设置为 0 个实例时的 AWS Codedeploy

postgresql - 在 AWS ec2 实例上通过 sqlalchemy to_sql 过程插入;面临位数据类型列的问题

node.js - 如何使用 Grunt 在 Visual Studio 2013 中调试 AngularJS 应用程序?

node.js - 为什么我无法访问 docker 容器中的 node.js 应用程序?

node.js - SocketIO 扩展架构和大房间需求