什么是Sec-WebSocket-Key?

时间:2013-08-16 02:55:55

标签: websocket

在第1.3节和第34节中;打开握手"在draft-ietf-hybi-thewebsocketprotocol-17中,它描述了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服务器。

6 个答案:

答案 0 :(得分:22)

根据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升级而不是期望它(比如通过手动添加相应的头,然后期待其他的)。禁止在浏览器中使用setRequestHeader方法设置Sec-WebSocket-Key和其他相关标头。

答案 1 :(得分:11)

主要用于缓存清除。

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

使用nonce(密钥)并且需要一个特定于WS的基本质询 - 响应,确保服务器实际理解这是一个WS握手,然后告诉客户端服务器确实正在监听端口。缓存反向代理永远不会实现那种散列逻辑错误"。

答案 2 :(得分:6)

我倾向于同意。

如果客户端忽略了Sec-WebSocket-Accept标头的值,那么重要性就不会改变。

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

标题的交换(例如,使用固定的'键和'接受'值)已经足以排除任何意外连接,而这些连接至少不会成为WebSocket服务器;如果它正在尝试,那么它进行此计算的要求几乎不会阻碍其成功。

RFC声称:

  

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

  

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

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

如果魔术GUID没有修复,这种交换会有所帮助,而是客户端和服务器之间的共享秘密。在这种情况下,交换机将允许服务器向客户端证明它具有共享秘密而不会泄露它。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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 :(得分:0)

服务器说:未定义索引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));
    }