【问题标题】:What is Sec-WebSocket-Key for?Sec-WebSocket-Key 有什么用?
【发布时间】:2013-08-18 09:02:41
【问题描述】:

draft-ietf-hybi-thewebsocketprotocol-17的1.3节“打开握手”中,对Sec-WebSocket-Key的描述如下:

为了证明握手被接收,服务器必须获取两条信息并将它们组合起来形成响应。第一条信息来自|Sec-WebSocket-Key|客户端握手中的头字段:

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

对于这个头域,服务器必须取值(在头域中,例如 base64 编码的 [RFC4648] 版本减去任何前导和尾随空格),并将其与全局唯一标识符(GUID , [RFC4122]) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 字符串形式,不太可能被不理解 WebSocket 协议的网络端点使用。然后在服务器的握手 [FIPS.180-2.2002] 中返回此连接的 SHA-1 哈希(160 位)、base64 编码(参见 [RFC4648] 的第 4 节)。

这是我无法理解的事情:为什么不简单地返回代码 101?如果正确使用 Sec-WebSocket-Key 是为了安全,或者为了证明他们可以处理 websocket 请求,那么任何服务器都可以根据需要返回预期的密钥,并假装自己是 WebSocket 服务器。

【问题讨论】:

  • 有没有人想到 Sec-WebSocket-Key HTTP 标头可以是公共 RSA 密钥?如果您了解 OpenSSL,显然反之亦然?这项技术是如此强大,以至于仅仅考虑它在浏览器上下文中的潜力就真的限制了它在宏伟计划中的潜力?
  • @suchislife 不,规范说这是为每次握手随机选择的 16 字节 b64 编码随机数。

标签: websocket


【解决方案1】:

根据 RFC 6455 Websocket 标准

first part:

.. the server has to prove to the client that it received the
client's WebSocket handshake, so that the server doesn't accept
connections that are not WebSocket connections.  This prevents an
attacker from tricking a WebSocket server by sending it carefully
crafted packets using XMLHttpRequest [XMLHttpRequest] or a form
submission.

...
For this header field, the server has to take the value (as present
in the header field, e.g., the base64-encoded [RFC4648] version minus
any leading and trailing whitespace) and concatenate this with the
Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
network endpoints that do not understand the WebSocket Protocol.

second part:

The |Sec-WebSocket-Key| header field is used in the WebSocket opening
handshake.  It is sent from the client to the server to provide part
of the information used by the server to prove that it received a
valid WebSocket opening handshake.  This helps ensure that the server
does not accept connections from non-WebSocket clients (e.g., HTTP
clients) that are being abused to send data to unsuspecting WebSocket
servers.

因此,由于标准中指定了 GUID 的值,因此不知道 Websockets 的服务器不太可能(可能,概率很小)会使用它。它不提供任何安全性(安全的 websockets - wss:// - 提供),它只是确保服务器理解 websockets 协议。

真的,正如您所提到的,如果您知道 websockets(这就是要检查的内容),您可以通过发送正确的响应来假装成为 websocket 服务器。但是,如果您的行为不正确(例如正确地形成帧),它将被视为违反协议。其实你可以写一个不正确的websocket服务器,但是用处不大。

另一个目的是防止客户端意外请求 websockets 升级而不期望它(例如,通过手动添加相应的标头,然后期待其他)。 Sec-WebSocket-Key等相关头信息禁止在浏览器中使用setRequestHeader方法设置。

【讨论】:

  • XMLHttpRequest中的javascript确实无法修改某些请求头字段,所以浏览器的方式被阻止了?但是如果有一个浏览器(可能是早期版本)可以允许修改Sec-websocket-key字段怎么办?或者如果请求是由程序发送来模拟浏览器的,您完全可以自己构建请求头,因此让 Sec-WebSocket-Key 无意义?
  • 请重新阅读我的答案。即使您编写将模拟正确升级请求的服务器或客户端,您也会有意在知道 Websockest 协议存在的情况下这样做。 Sec-WebSocket-Key 用于过滤 unintended 请求。如果您担心安全问题 - 请使用安全的 websocket。
  • 所以基于ppl设计的协议可以正常使用,如果遇到恶意软件编写者会像普通http一样搞砸,我理解对了吗?
  • 当然,如果你知道它是如何工作的,你可以打破协议的正常流程。正是为了这个目的(如果你想确保一切都在你的控制之下)密码学和安全协议被提出。这个想法是分离特定格式的数据传输(因为 HTTP 和 Websocket 协议负责)和安全性(这里是 SSL/TLS),因为最新增加了一些可能不必要的性能开销。并且结合这些提供了安全的数据传输 - 并命名为 HTTPS/WSS。
  • @PavelK 我正在为 Windows 开发一个客户端,并将其传播到不同的机器上。从客户的角度来看,坚持使用固定的编码密钥是一种好习惯吗?我的意思是,如果 base64encoded 中的这个字段......那么解码的原始字符串是什么?一个随机值?
【解决方案2】:

主要用于缓存清除。

想象一个透明的反向代理服务器监视 HTTP 流量。如果它不理解 WS,它可能会错误地缓存 WS 握手并以无用的 101 回复下一个客户端。

使用高熵密钥并要求基本的挑战-响应相当特定于 WS,可确保服务器真正理解这是 WS 握手,并反过来告诉客户端服务器确实会在端口上侦听。缓存反向代理永远不会“错误地”实现该散列逻辑。

【讨论】:

  • 反向代理服务器不应该认为 101 响应是可捕获的,所以 imo 这不是一个强有力的理由。
  • @nhooyr 你是对的。如果我的(肯定是推测性的)答案对基本原理是正确的,那么这显然是针对编程错误的软件的一种非常防御性的措施。鉴于这些可能在客户端和服务器控制之外的路径上,在我看来这是一个合理的防御措施。
  • @nhooyr 否。通过earlier HTTP standard,代理可以缓存 HTTP 101(和所有其他结果)。这个答案似乎是正确的。
  • 此外,阅读第一个 WebSocket RFC,看起来设计者为(智能)缓存 WebSocket 流量敞开了大门,尽管看起来很奇怪。
【解决方案3】:

我倾向于同意。

如果客户端忽略 Sec-WebSocket-Accept 标头的值,任何重要的事情都不会改变。

为什么?因为服务器没有通过执行此计算来证明任何事情(除了它具有执行计算的代码)。它唯一排除的只是一个简单地回复预设响应的服务器。

标头的交换(例如,具有固定的 'key' 和 'accept' 值)已经足以排除与至少不是试图成为 WebSocket 服务器的东西的任何意外连接;如果它正在尝试,那么它执行此计算的要求几乎不会成为其成功的障碍。

RFC 声明:

".. 服务器必须向客户端证明它收到了 客户端的 WebSocket 握手,使服务器不接受 不是 WebSocket 连接的连接。”

和:

“这有助于确保服务器不接受来自非 WebSocket 客户端的连接..”

这些说法都没有任何意义。服务器永远不会拒绝连接,因为它是计算哈希的服务器,而不是检查它的服务器。

如果魔法 GUID 不是固定的,而是客户端和服务器之间共享的秘密,这种交换将是有意义的。在这种情况下,交换将允许服务器向客户端证明它拥有共享秘密而不泄露它。

【讨论】:

    【解决方案4】:

    RFC 6455 规范显示了服务器响应客户端(浏览器)所需的(最少)4 行代码。最难的部分是确认您的 Websocket 服务器 C 代码正在执行正确的计算。这是一个简短的 PHP 脚本(PHP 很容易在所有操作系统上安装),它将正确计算要回复的密钥。将您从客户端(浏览器)获得的密钥硬编码到下面的第二行:

    <?php
        $client_websocket_key = "IRhw449z7G0Mov9CahJ+Ow==";
        $concat = $client_websocket_key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
        $ascii_sha1 = sha1( $concat );  // print this one for debugging, not used for real value.
        $sha1 = sha1( $concat, true );
        echo base64_encode( $sha1 );
    ?>
    

    【讨论】:

      【解决方案5】:

      RFC 不清楚的是,来自客户端的“Sec-WebSocket-Key”标头在每个请求上应该是随机的。这意味着来自代理的任何缓存结果都将包含无效的“Sec-WebSocket-Accept”回复标头,因此 websocket 连接将失败,而不是无意中读取缓存数据。

      【讨论】:

        【解决方案6】:

        服务器说:未定义索引 Sec-WebSocket-Key。 当我从手机访问该网站时。从同一台电脑(本地主机)没有这样的问题,解决办法是什么:

        function doHandshake($received_header,$client_socket_resource, $host_name, $port) {
                $headers = array();
                $lines = preg_split("/\r\n/", $received_header);
                foreach($lines as $line)
                {
                    $line = chop($line);
                    if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
                    {
                        $headers[$matches[1]] = $matches[2];
                    }
                }
        
                $secKey = $headers['Sec-WebSocket-Key'];
                $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
                $buffer  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
                "Upgrade: websocket\r\n" .
                "Connection: Upgrade\r\n" .
                "WebSocket-Origin: $host_name\r\n" .
                "WebSocket-Location: ws://$host_name:$port/demo/shout.php\r\n".
                "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
                socket_write($client_socket_resource,$buffer,strlen($buffer));
            }
        

        【讨论】:

          猜你喜欢
          • 2016-06-28
          • 1970-01-01
          • 1970-01-01
          • 2013-08-15
          • 1970-01-01
          • 1970-01-01
          • 2015-10-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多