本章描述了一些适用于 WebSocket 协议的安全注意事项。 具体的安全注意事项在本章的子章节描述。
WebSocket 协议防止恶意的 JavaScript 运行在一个受信任的应用内部,比如web浏览器,例如,通过检查头字段|Origin|(见下文)。 更多细节请参考 1.6 节。 在一个更强大的客户端的情况下,这样的假设不成立。
虽然该协议的目的是被 web 页面中的脚本使用,它也可以被主机直接使用。 这样的主机按照它们自己的行为行事,因此可以发送伪造的|Origin|头字段,骗过服务器。 因此服务器应该小心,假设它们是直接与来自已知源的脚本通信,且必须考虑到它们可能以非预期方式访问。 尤其,服务器不应该相信任何输入是有效的。
例如:如果服务器使用输入作为 SQL 查询的一部分,所有输入文本在传输到 SQL 服务器之前应该被转义,以免服务器受到 SQL 注入攻击。
服务器不打算处理来自任意 web 页面的输入,但仅限于网站应该验证 |Origin| 头是一个它们盼望的源。 如果源指示是服务器不可接受的,那么它应该以一个包含 HTTP 403 Forbidden 的状态码的回复响应 WebSocket 握手。
当不受信任方通常是一个执行在受信任的客户端上下文中的 JavaScript 应用的作者时,|Origin|头字段可以保护攻击的情况。 客户端本身可以与服务器联系,并通过| Origin|头字段机制,决定是否提供JavaScript应用的这些通信权限。 目的不是为了阻止非浏览器建立连接,而是确保受信的浏览器在潜在的恶意 JavaScript 控制下不能伪造 WebSocket 握手。
除了端点是 WebSocket 攻击的目标之外,web基础设施的其他部分,如代理,也可能是攻击的对象。 在本协议正在开发时,进行了一个实验旨在演示一类代理上的攻击,其导致部署在野的缓存代理中毒[讨论]。 一般的攻击形式是在“攻击者”的控制下建立一个到服务器的连接,执行类似于 WebSocket 协议建立连接的 HTTP 连接上的 UPGRADE,且随后在已经 UPGRADE 的连接上发送数据,看起来像一个 GET 请求一个特定的已知资源(在一次攻击中,很可能会像广泛部署的用于跟踪点击或一个广告服务网络资源的脚本)。 远程服务器响应的东西看起来像是到伪造的GET请求的一个响应,且这个响应将被一个非零百分比的部署的中间件缓存,因此使缓存中毒了。 这种攻击的静效应将是如果能说服用户去访问攻击者控制的网站,攻击者可能使用户和其他晚于相同缓存的用户的缓存中毒且在其他源运行恶意脚本,影响网络安全模型。
为避免这种部署的中间件上的攻击,前置不兼容 HTTP 的和帧一起的应用提供的数据是不够的,因为无法详尽地发现和测试每一个不符合中间件的不跳过这样的非 HTTP 帧和错误地假装帧负载。
因此,采用的防御是掩码所有从客户端到服务器端发送的数据,使远程脚本(攻击者)无法控制数据如何在电线上发送,从而无法构造一个可能被中间件误解释的作为一个 HTTP 请求的消息。
客户端必须为每一帧选择一个新的掩码密钥,使用一个不能被提供数据的终端应用预测。 例如,每次掩码可以从一个强加密的随机数生成器获取。 如果使用相同的密钥或存在一个可预测的模式用于选择下一个密钥,当掩码后,攻击者可以发送一个消息,可能作为一个 HTTP 请求出现(通过交换消息,攻击者希望观察线上并用下一个将被使用的掩码密钥掩码它,当客户端应用它时掩码密钥将有效的解码数据)。 一旦从客户端传输一个帧已经开始,帧的负载(应用提供的数据)必须不能被应用修改也是必要的。
否则,攻击者可能发送一个已知初始数据(如都是0)的长帧,一收到数据的第一部分后就开始计算使用的掩码密钥,当掩码后,接着修改尚未发送的作为一个请求出现的帧中的数据(这本质上是和前面段落描述的使用一个已知的或可预测的掩码密钥是相同的问题)。 如果需要发送额外的数据或要发送的数据以某种方式修改了,新的或修改了的数据必须在一个新的帧中发送,那么需要一个新的掩码密钥。 总之,一旦开始传输一个帧,对于远程脚本(应用)来说,内容必须不能是可修改的。
威胁模型用来保护客户端发送的在一个请求出现的数据。 因此,需要掩码的信道是从客户端到服务器的数据。 服务器到客户端的数据可以作出看起来像一个响应,但为了完成这个请求,客户端也必须有能力去伪造一个请求。 因此,没必要在两个方向上掩码数据(从客户端到服务器的数据没有掩码)。
尽管掩码提供了保护,对于客户端和服务器没有掩码的这种类型的中毒攻击,非兼容 HTTP 代理将依然是脆弱的。
实现已经实现— 和/或特定平台的有关帧大小或总消息大小的限制,从多个帧重新组装后,必须保证它们自己不超过这些限制。 (例如,一个恶意终端无论是通过单一的大帧(例如,2**60大小)还是通过发送一个长流的分片消息的一部分的小帧,可以设法耗尽它的对等体端点(Peer,即要攻击的那一方)的内存或安装一个拒绝服务攻击)。 这样的实现应该对帧大小和和从多个帧重组后的总消息大小加以限制。
本规范没有规定任何特定的方式在 WebSocket 握手期间服务器可以验证客户端。 WebSocket 服务器可以使用任何普通 HTTP 服务器可用的客户端验证机制,如Cookie,HTTP 验证,或者 TLS 验证。
连接的保密性和完整性是通过运行在 TLS(wss URI)上 WebSocket 协议提供的。 WebSocket 实现必须支持 TLS 并应该在与它们的对等端点通信时使用它。
对于使用 TLS 的连接,TLS 提供的受益量在很大程度上取决于在 TLS 握手期间协商的算法强度。 例如,一些 TLS 加密机制不提供连接的保密性。 为了实现合理级别的保护,客户端应该仅适用强 TLS 算法。 “Web安全上下文:用户接口指南”[W3C.REC-wsc-ui-20100812]讨论了强TLS算法的构成。[RFC5246]的附录A.5和附录D.3中提供了额外的指导。
传入的数据必须始终由客户端和服务器验证。 如果,在任何时候,一个端点不理解它的数据或违反了一些端点确定的安全输入标准,或当端点看到一个打开阶段握手没有符合它盼望的值(例如,在客户端请求中不正确的路径或源),端点可以终止 TCP 连接。 如果在 WebSocket 握手成功后接收到了无效数据,端点应该在进行 _关闭WebSocket连接_ 之前发送一个带有适当状态码(7.4节)的关闭帧。 使用一个带有适当状态码的关闭帧能帮助诊断问题。 如果在 WebSocket 握手期间发送了无效的数据,服务器应该返回一个适当的 HTTP[RFC2616]状态码。 使用错误的编码发送文本数据是通常出现的一类安全问题。 本协议规定一个 Text 数据类型(而不是Binary或其他类型)的消息包含 UTF-8 编码的数据。 虽然仍指定了长度,且实现本协议的应用应该使用长度来决定帧从哪真正结束,但以一个不当的编码发送数据仍可能打破建立在本协议之上的应用的假设,导致从误解释数据到丢失数据或潜在的安全漏洞。
本文档中描述的 WebSocket 握手不依赖于任何 SHA-1 安全特性,例如抗碰撞性或抗第二前像攻击(如同[RFC4270]中的描述)。