如何从Javascript设置WebSocket Origin Header?

时间:2015-05-09 19:39:23

标签: javascript websocket cross-domain

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

以下是连接套接字的javascript代码:

Origin: test.com

如何使用javascript启动与ws = new WebSocket("123.123.123.123"); 的不诚实Origin标头的websocket连接?

我希望这样的东西可行,但我找不到任何这样的东西:

Origin: test.com

4 个答案:

答案 0 :(得分:3)

简单的解决方案是在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 :(得分:2)

  

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

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

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

答案 2 :(得分:0)

如果你真的必须伪造一个假的来源'来自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;

}

答案 3 :(得分:0)

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