WebSocket通过Amazon ELB或直接(远程IP问题)

时间:2012-12-26 10:00:39

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

我们使用WebSockets与我们的EC2实例进行通信。 我们的脚本使用nodejs和Express提供,然后初始化WebSocket。 现在使用ELB,这使得生活更难以识别客户端IP。 使用x-forwarded-for标头我们可以在HTTP的上下文中获取IP,但是当它涉及服务器中的WebSocket上下文时,看起来它不会被亚马逊转发。

我们确定了2个选项:

  1. 直接与实例(使用其公共DNS)通信WebSocket。
  2. 维护某种sessionid,其中在HTTP的上下文中存储IP并将其与sessionid相关联。客户端将使用HTTP响应获取其sessionid,并将其用于WebSockets。服务器将识别客户端并从缓存中解析其IP。
  3. 两个选项都不是很好:1不是容错的,2是复杂的。 还有更多解决方案吗?亚马逊可以以某种方式转发IP吗?什么是最佳做法?

    由于

2 个答案:

答案 0 :(得分:1)

我使用过websockets并且我使用过ELB,但是我从来没有和它们一起工作过,所以我没有意识到Elastic Load Balancer上的HTTP转发器不理解websocket请求... < / p>

所以我认为你必须使用TCP转发器,这解释了为什么你使用不同的端口,当然TCP转发器是协议不知道的,因此它根本不会添加任何标头。 / p>

一个似乎相当通用且不复杂的选项是应用程序的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的查询字符串,您可以使用超级密钥对base64_decode然后aes_decrypt在查询参数“client”中找到的字符串,然后验证它以“ipv4:”开头。 ..如果没有,那么这不是合法的价值。

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

当然,使用此设置的问题是它需要重放:给定的客户端IP地址将始终生成相同的值。如果您仅将客户端IP地址用于“信息用途”(例如日志记录或调试),那么这可能就足够了。如果您将它用于任何更重要的事情,您可能希望扩展此实现 - 例如,通过添加时间戳:

'ipv4:192.168.1.1;valid:1356885663;' 

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

这些建议都取决于你动态生成websocket url的能力,浏览器​​与它连接的能力,以及你能够在websocket请求中访问URL的查询字符串部分...但是如果这些部分会掉落到位,也许这会有所帮助。


其他想法(来自评论):

我在上面建议的时间戳为seconds from the epoch,它为您提供了一个递增计数器,在您的平台中不需要任何状态 - 它只需要您的所有服务器时钟都是正确的 - 所以它不会添加不必要的复杂如果解密的值包含的时间戳小于(例如)5秒与服务器当前时间不同(+/-),那么您就知道您正在处理经过身份验证的客户端。允许的时间间隔只需要与客户端在加载原始页面后尝试其websocket连接的最大合理时间一样长,加上所有服务器时钟的最大偏差。

当然,对于NAT,多个不同的用户可能位于相同的源IP地址之后。用户实际上可以从不同的源IP进行websocket连接,而不是它们发起第一个http连接的情况,这也是事实,尽管远不那么可能,但它仍然是非常合法的...而且听起来像是对于您而言,身份验证用户可能比实际源IP更重要。

如果您还在加密字符串中包含经过身份验证的用户ID,则您拥有原始IP,用户帐户和时间唯一的值,精度为1秒。我认为这是你所指的额外的盐。将用户帐户添加到字符串应该可以获得您想要的信息。

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

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

答案 1 :(得分:0)

这取决于申请的严重程度。

基于客户端IP地址做出任何决定是一个冒险的主张。基于它的安全性,更是如此。虽然到目前为止提供的建议在给定的约束条件下运行良好,但对于强大的企业应用程序来说还不够。

正如已经指出的那样,NAT可以掩盖客户端IP地址。因此,从他们的工作地点访问Web的人通常看起来具有相同的IP地址。人们在家里的路由器充当NAT,因此家中访问网络的每个家庭成员都会看到具有相同的IP地址。甚至是从PC和平板电脑访问应用程序的同一个人......

无论是否在NAT后面,在同一台机器上使用来自两个浏览器的应用程序将显示具有相同的地址。同样,同一浏览器中的多个标签似乎具有相同的地址。

代理或负载均衡器等其他交接点也可能隐藏原始客户端IP地址,使代理/负载均衡器后面的东西认为它们是客户端。 (更复杂或更低级别的中介可以阻止这种情况,这使得它们更复杂或更昂贵。)

鉴于上述所有情况,严肃的应用程序不应依赖客户端IP地址进行任何重要决策,特别是在安全性方面。