WebSocket通过亚马逊ELB或直接(远程IP问题)

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

我们确定了2个选项:

  1. 直接与实例(使用其公共DNS)通信WebSocket。
  2. 保持某种sessionid,在HTTP中存储IP,并将其与sessionid相关联。 客户端将使用HTTP响应来获取其sessionid,并将其用于WebSocket上。 服务器将识别客户端并从caching中parsing其IP。

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

谢谢

我曾与websockets,我已经与ELB合作,但我从来没有一起工作,所以我没有意识到,Elastic Load Balancer上的HTTP转发器不明白websocket请求…

所以我认为你必须使用TCP转发器,这就解释了为什么你使用不同的端口,当然TCP转发器是不知道协议的,所以它不会添加任何头文件。

一个看起来相当通用并且不复杂的选项是应用程序的http端通过推送信息来通知websocket端而不是将其存储在caching中以供检索。 它的可扩展性和轻量级,假设在您的环境中没有障碍,使得实现困难或不可能。

在生成加载websocket的web页面时,将string“ipv4:”和客户端的IP(例如“192.168.1.1”)连接并encryption,并使结果更加友好:

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

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

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

然后,当为包含websocket的页面呈现html时,dynamic构buildurl:

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

假设您的代码可以访问websocket的查询string,您可以base64_decode,然后使用超级密钥对查询参数“client”中find的stringaes_decrypt,然后validation它是否以“ipv4:”开头… if它不,那不是合法的价值。

当然,“ipv4:”(在string的开始处)和“客户端”(对于查询参数)是任意的select,并没有任何实际意义。 我select的128位AES也是任意的。

当然,这个设置的问题在于重播:给定的客户端IP地址将始终生成相同的值。 如果您只将客户端IP地址用于“信息目的”(例如日志logging或debugging),那么这可能就足够了。 如果您使用它更重要,则可能需要扩展此实现 – 例如,通过添加时间戳:

 'ipv4:192.168.1.1;valid:1356885663;' 

在接收端,解码string并检查时间戳。 如果它不是+/-你认为安全的时间在几秒钟内,那么不要相信它。

这些build议都取决于你dynamic生成websocket url的能力,浏览器​​连接它的能力,以及你能够访问websocket请求中的URL查询string部分……但是如果这些部分将落实到位,也许这会有所帮助。


额外的想法(来自评论):

上面我build议的时间戳是从纪元开始的秒数 ,它给了你一个递增的计数器,在你的平台上不需要有状态 – 它只要求所有的服务器时钟都是正确的,所以它不会增加不必要的复杂性。 如果解密的值包含小于(例如)与服务器当前时间不同(+/-)5秒的时间戳,则您知道您正在处理经过身份validation的客户端。 允许的时间间隔只要是客户在加载原始页面之后尝试连接WebSocket的最大合理时间,再加上所有服务器时钟的最大偏差即可。

当然,使用NAT,多个不同的用户可能位于相同的源IP地址之后。 同样的事实,虽然不太可能,但是用户实际上可以从一个不同的源IP创buildwebsocket连接,而不是他们发起第一个http连接的那个,并且仍然是相当合法的……听起来像authentication用户可能比实际的源IP更重要。

如果您在encryptionstring中也包含经过身份validation的用户标识,那么您将拥有一个对原始IP,用户帐户和时间唯一的值,其精度为1秒。 我认为这是你所说的额外的盐。 将用户帐户添加到string应该会得到您想要的信息。

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

TLS应防止未经授权方发现此encryptionstring,但避免重播也很重要,因为生成的URL在用户浏览器的html页面的“查看源代码”中以明文forms提供。 您不希望今天被授权的用户,但明天未经授权的用户可以使用已被认定为不再有效的已签名string来欺骗他们。 键入一个时间戳,并要求它在一个非常小的有效窗口中防止这一点。

这取决于应用程序的严重程度。

基于任何一种客户IP地址的决定是一个冒险的主张。 基于它的安全性,更是如此。 尽pipe迄今为止提供的build议在给定的限制范围内运行良好,但对于强大的企业应用程序来说还不够。

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

无论是否在NAT之后,使用来自同一台机器上两个浏览器的应用程序将显示具有相同的地址。 同样,同一浏览器中的多个选项卡将显示为具有相同的地址。

像代理或负载平衡器的其他交汇点也可能隐藏原始客户端IP地址,以使代理/负载均衡器背后的东西认为他们是客户端。 (更复杂或更低层次的中介可以防止这种情况,这使得他们更复杂或更昂贵。)

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