为什么 websockets 不支持自定义标头?

信息安全 网络套接字
2021-08-20 17:37:43

这个问题与我问的这个问题有关

总而言之,我目前正在使用 websockets,并且试图了解如何验证使用 websocket 连接连接到服务器的客户端。

在正常连接上,我使用基于令牌的身份验证,我基本上只是在登录后从服务器获取一个令牌。每次我向服务器发出请求时,我都会将它放在一个名为 Authentication 的自定义标头中,我的服务器会从中读取它那里。

对于 websockets,这不起作用,因为 websockets 没有自定义标头。我有两个选择来传递这个令牌。

1)将令牌放在查询字符串中 - 显然不是一个很好的选择。令牌可以由服务器等记录。

2) 将令牌放入 cookie - 这有效,但仅当客户端和服务器位于同一域时。还有其他限制,例如客户端必须是浏览器并支持 cookie 等。

无论如何,另一个问题是试图找到解决方案,这个问题是关于理解为什么这是一个问题。为什么 websockets 不支持自定义标头?这不太可能是疏忽——websockets 和基于令牌的身份验证都是相当成熟的技术。在 websocket 连接期间允许自定义标头是否存在某种安全问题?

2个回答

为什么 websockets 不支持自定义标头?不太可能是疏忽...

我认为这可能一个疏忽。

这实际上并不让我感到惊讶,因为在我看来,浏览器内的 WebSockets 的实现一开始就没有经过深思熟虑。最明显的问题是 WebSockets 忽略了相同的来源策略或CORS限制,即服务器需要明确检查连接的来源(Origin标头),否则可能会发生跨站点 WebSocket 劫持

这种不安全的默认设计与 CORS 的默认安全行为形成对比,CORS 的创建时间比 WebSockets 早一点。鉴于这个明显的安全问题,我怀疑是否有任何安全考虑在不允许像 XHR 中那样自定义标头方面发挥了作用。

请注意,WebSockets 协议本身支持自定义标头,因为它以类似于普通 HTTP 请求的 HTTP 握手开始。只有浏览器 API 缺少使用自定义标头的能力。

Steffen 已经回答了您直接提出的问题,但由于您的问题上下文是“您如何验证客户端”,请考虑以下问题:任何网络客户端(HTTP、SSH、SMTP、RDP 等)如何进行身份验证?

它几乎总是一个两步过程,从一个非常高级的抽象来看,它看起来像这样:

  1. 客户端建立到服务器的连接(从技术上讲,这一步对于基于 UDP 和其他无连接协议的协议是可以跳过的,但即便如此,通常也会有某种“客户端问候”步骤)。
  2. 客户端向服务器发送一些身份验证数据(可能是密码、令牌、API 密钥、公钥加上用相应私钥签名的一些数据,或者......),可能是为了响应一些挑战服务器发送过来。

从这个角度来看,解决方案很简单。您的 WebSocket 服务器接受所有传入请求(或执行一些验证,例如来源,但没有真正验证客户端),然后客户端通过 websocket 传输其身份验证。服务器在从客户端接收到身份验证数据并对其进行验证之前,不会让套接字用于任何事情。如果您不想在服务器上为每个 WS 存储状态,只需将身份验证数据(原始表单或客户端在初始身份验证后发回的会话令牌)与每条消息一起发送。(也就是说,毕竟 cookie 是什么。)

它会使您的 WS 服务器代码稍微复杂一些。突然之间,您必须区分经过身份验证和未经身份验证的连接,并有一种在它们之间切换的方法,并在未经身份验证的状态下禁用经过身份验证的功能。您必须添加新消息,或至少更改消息格式,才能传输身份验证数据。但归根结底,这是完全可能的。一个已建立的 websocket 基本上只是一个 TCP 连接(希望通过 TLS 建立隧道),并且人们已经通过 TCP 传输身份验证数据(在建立连接之后)已经有半个多世纪了。