【问题标题】:Websocket handshake: Incorrect 'Sec-WebSocket-Accept' header valueWebsocket 握手:不正确的“Sec-WebSocket-Accept”标头值
【发布时间】:2019-02-22 16:35:53
【问题描述】:

我正在用 C++ 编写一个 websocket 服务器,但无法让握手工作。 Chrome 报告错误是由于接受标头错误,但我相信该值是正确的。

作为一个示例交换,客户端发送以下密钥:

Sec-WebSocket-Key: ypX0m2zum/pt80mxlVo8PA==

我的服务器发回:

Sec-WebSocket-Accept: Kl4mnqm5QA6bBmGf3EAN0nyGXws=

我已经根据 RFC 中的示例测试了我的服务器,并且它检查出来了。我不知道为什么它不被接受。我的理论是,我一定是在做其他事情,它会产生与错误接受值相同的错误。

这是来自wireshark捕获的不同请求:

Hypertext Transfer Protocol
    GET /websocket HTTP/1.1\r\n
    Host: 127.0.0.1:8443\r\n
    Connection: Upgrade\r\n
    Pragma: no-cache\r\n
    Cache-Control: no-cache\r\n
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Upgrade: websocket\r\n
    Origin: chrome-extension://eajaahbjpnhghjcdaclbkeamlkepinbl\r\n
    Sec-WebSocket-Version: 13\r\n
    Accept-Encoding: gzip, deflate, br\r\n
    Accept-Language: en-US,en;q=0.9\r\n
    Sec-WebSocket-Key: +zJ3/KI/Zrumgh+AjxopRQ==\r\n
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
    \r\n
    [Full request URI: http://127.0.0.1:8443/websocket]
    [HTTP request 1/1]
    [Response in frame: 6]

下面是回复:

Hypertext Transfer Protocol
    HTTP/1.1 101 Switching Protocols\r\n
    Upgrade: websocket\r\n
    Connection: Upgrade\r\n
    Sec-WebSocket-Accept: anTEIFyI/gTepr8Q3okBj81M2/4=\r\n
    \r\n
    [HTTP response 1/1]
    [Time since request: 0.000245010 seconds]
    [Request in frame: 4]

有人能告诉我回复有什么问题吗?我的接受值不正确吗?

编辑 1:

我用来创建响应值的代码。 websocket_key 是从之前的请求中获取的。

    const char *magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

    int pre_hash_size = 36 + websocket_key.size();
    char pre_hash[pre_hash_size];

    memcpy(pre_hash, websocket_key.c_str(), websocket_key.size());
    memcpy(pre_hash + websocket_key.size(), magic_string, 36);

    unique_ptr<Botan::HashFunction> hash1(Botan::HashFunction::create("SHA-1"));
    Botan::secure_vector<uint8_t> post_hash = hash1->process(reinterpret_cast<const uint8_t *>(pre_hash), pre_hash_size);

    string accept_response = base64_encode(post_hash.data(), post_hash.size());

这是base 64函数:

/* 
   base64.cpp and base64.h
   base64 encoding and decoding with C++.
   Version: 1.01.00
   Copyright (C) 2004-2017 René Nyffenegger
   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.
   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:
   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.
   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.
   3. This notice may not be removed or altered from any source distribution.
   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/

static const std::string base64_chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789+/";

std::string base64_encode(unsigned char const *bytes_to_encode, unsigned int in_len)
{
  std::string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--)
  {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3)
    {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for (i = 0; (i < 4); i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for (j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while ((i++ < 3))
      ret += '=';
  }

  return ret;
}

【问题讨论】:

  • 你能不能也显示你用来生成值的代码?
  • @Ferrybig 编辑已添加。

标签: websocket


【解决方案1】:

问题在于,当我连接来自 websocket 密钥(由客户端发送)和魔术字符串(常量)的 pre_hash 字符串时,我没有考虑到 size() 函数包含的 空终止符这是计数。 我在解析请求头时无意中添加了一个额外的空间。

记住小子,C++ 字符串是空终止的,size() 反映了这一点。

【讨论】:

  • C++ 字符串不是空终止符(但如果需要,您可以从std::string获取一个空终止符的 C 字符串),并且 std::string::size() 不包含任何空终止符在字符串数据之后,但它会在字符串数据中包含任何 embedded 空值,在这种情况下这将是一个错误,因为根本不应该存在任何空值。如果有,您在填充 std::strings 时做错了。
  • 大声笑,应该查一下。感谢@RemyLebeau 抓住了这一点。我用真正的原因更新了答案。
猜你喜欢
  • 2016-04-19
  • 2015-06-17
  • 1970-01-01
  • 2021-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
相关资源
最近更新 更多