【问题标题】:How to set WebSocket Origin Header from Javascript?如何从 Javascript 设置 WebSocket Origin Header?
【发布时间】:2015-05-09 19:39:23
【问题描述】:

我正在尝试使用 javascript 从本地 test.dev 页面向在 ip 123.123.123.123 代表 test.com 运行的服务器发出 websocket 请求。请求通过,但123.123.123.123 服务器在 websocket 请求中看到 Origin: test.dev 标头并拒绝连接,因为它想看到 Origin: test.com

这里是连接socket的javascript代码:

ws = new WebSocket("123.123.123.123");

如何使用 javascript 启动带有不诚实的 Origin 标头的 Origin: test.com 的 websocket 连接?

我希望这样的东西能起作用,但我找不到这样的东西:

ws = new WebSocket("123.123.123.123", "test.com");

【问题讨论】:

    标签: javascript websocket cross-domain


    【解决方案1】:

    简单的解决方案是在您的hosts 文件中创建一个条目以将test.com 映射到123.123.123.123。稍后当您想连接“真实”test.com 时,您需要删除此条目。

    一个不那么老套的解决方案需要使用代理,它可以即时为您重写标题。考虑在您的系统上安装nginx,然后将请求代理到123.123.123.123,保持所有内容相同除了 的Origin 标头。这是您在 nginx 配置文件中需要的条目:

    server {
        server_name test.dev;
    
        location / {
            proxy_pass http://123.123.123.123;
            proxy_set_header Origin test.com;
    
            # the following 3 are required to proxy WebSocket connections.
            # See more here: http://nginx.com/blog/websocket-nginx/
    
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
    

    【讨论】:

    • +1 使用 nginx 是另一个不错的选择,如果设置起来比使用专用的 Web 调试代理要复杂一些。
    • 好的,我已经有一些类似的代理,所以再设置一个应该不会太难。感谢您提供详尽的示例!我希望它代理到任意 ip,这可以通过 nginx 请求变量来完成。
    【解决方案2】:

    如何使用 javascript 启动带有不诚实的 Origin 标头的 Origin: test.com 的 websocket 连接?

    如果我们可以在 JavaScript 中伪造请求的来源,那么同源策略就不能很好地保证我们的安全。它的存在只是为了保护我们免受这种和其他潜在攻击媒介的侵害。

    由于这看起来像是开发工作,您是否考虑过使用 Web 调试代理,例如 Fiddler(免费)或 Charles(付费)?有了这些,您可以为您自己的机器或通过调试器代理的任何测试机器修改 WebSocket 的初始握手请求或响应。​​

    【讨论】:

    • 我想这是有道理的,它可以保护用户免受不良 js 的侵害,即使它不能保护服务器免受不良客户端的侵害。我想我会使用 nginx,但那些易于使用的代理看起来可能对其他人有用。谢谢!
    【解决方案3】:

    一个好的解决方案是用另一个 websocket 库覆盖 WebSocket 调用,例如 https://github.com/websockets/ws。要在浏览器中使用这个 node.js 库,你只需要使用http://browserify.org/

    【讨论】:

    • AlsorejectUnauthorized: 当服务器证书无效时,false 可以帮助下一个 2 源选项...
    【解决方案4】:

    如果您真的必须从 javascript 中伪造一个虚假的“来源”标头值 - 有办法。您不会在普遍接受的原则手册中找到它,但它是:

    创建一个调用套接字的 php 文件,并使用伪造的原始值。现在使用 ajax 从你的 javascript 调用 php 文件。

    它可能不优雅、不道德或不被接受,但永远不要接受任何人告诉你它做不到。

    'send.php' 是使用来自 javascript 的 ajax 调用的

    send.php 内容

    require "websocket_client.php";
    $client = new Client("IP_ADDR:PORT", $_GET[mobile] ."|".$_GET[login]);
    $client->send('1112223333|sms');
    $client->send(json_encode(array('login'=>$user,'msg'=>$msg)));
    echo $client->receive();`enter code here`
    

    websocket_client.php 是一个具有基本 websocket 功能的类文件(包括自定义源值

     /**
    
    
     * Perform WebSocket handshake
       */
      protected function connect() {
        $url_parts = parse_url($this->socket_uri);
        $scheme    = $url_parts['scheme'];
        $host      = $url_parts['host'];
        $user      = isset($url_parts['user']) ? $url_parts['user'] : '';
        $pass      = isset($url_parts['pass']) ? $url_parts['pass'] : '';
        $port      = isset($url_parts['port']) ? $url_parts['port'] : ($scheme === 'wss' ? 443 : 80);
        $path      = isset($url_parts['path']) ? $url_parts['path'] : '/';
        $query     = isset($url_parts['query'])    ? $url_parts['query'] : '';
        $fragment  = isset($url_parts['fragment']) ? $url_parts['fragment'] : '';
    
    $path_with_query = $path;
    if (!empty($query))    $path_with_query .= '?' . $query;
    if (!empty($fragment)) $path_with_query .= '#' . $fragment;
    
    if (!in_array($scheme, array('ws', 'wss'))) {
      throw new BadUriException(
        "Url should have scheme ws or wss, not '$scheme' from URI '$this->socket_uri' ."
      );
    }
    
    $host_uri = ($scheme === 'wss' ? 'ssl' : 'tcp') . '://' . $host;
    
    // Open the socket.  @ is there to supress warning that we will catch in check below instead.
    $this->socket = @fsockopen($host_uri, $port, $errno, $errstr, $this->options['timeout']);
    
    if ($this->socket === false) {
      throw new ConnectionException(
        "Could not open socket to \"$host:$port\": $errstr ($errno)."
      );
    }
    
    // Set timeout on the stream as well.
    stream_set_timeout($this->socket, $this->options['timeout']);
    
    // Generate the WebSocket key.
    $key = self::generateKey();
    
    // Default headers (using lowercase for simpler array_merge below).
    $headers = array(
      'host'                  => $host . ":" . $port,
      'user-agent'            => 'websocket-client-php',
      'connection'            => 'Upgrade',
      'upgrade'               => 'websocket',
      'origin'               =>  $MY_CUSTOM_SHADY_VALUE,
      'sec-websocket-key'     => $key,
      'sec-websocket-version' => '13',
    );
    
    // Handle basic authentication.
    if ($user || $pass) {
      $headers['authorization'] = 'Basic ' . base64_encode($user . ':' . $pass) . "\r\n";
    }
    
    // Deprecated way of adding origin (use headers instead).
    if (isset($this->options['origin'])) $headers['origin'] = $this->options['origin'];
    
    // Add and override with headers from options.
    if (isset($this->options['headers'])) {
      $headers = array_merge($headers, array_change_key_case($this->options['headers']));
    }
    
    $header =
      "GET " . $path_with_query . " HTTP/1.1\r\n"
      . implode(
        "\r\n", array_map(
          function($key, $value) { return "$key: $value"; }, array_keys($headers), $headers
        )
      )
      . "\r\n\r\n";
    
    // Send headers.
    $this->write($header);
    
    // Get server response.
    $response = '';
    do {
      $buffer = stream_get_line($this->socket, 1024, "\r\n");
      $response .= $buffer . "\n";
      $metadata = stream_get_meta_data($this->socket);
    } while (!feof($this->socket) && $metadata['unread_bytes'] > 0);
    
    /// @todo Handle version switching
    
    // Validate response.
    if (!preg_match('#Sec-WebSocket-Accept:\s(.*)$#mUi', $response, $matches)) {
      $address = $scheme . '://' . $host . $path_with_query;
      throw new ConnectionException(
        "Connection to '{$address}' failed: Server sent invalid upgrade response:\n"
        . $response
      );
    }
    
    $keyAccept = trim($matches[1]);
    $expectedResonse
      = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
    
    if ($keyAccept !== $expectedResonse) {
      throw new ConnectionException('Server sent bad upgrade response.');
    }
    
    $this->is_connected = true;
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-03
      • 2011-04-06
      • 2014-11-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 2012-03-05
      相关资源
      最近更新 更多