如何从 Javascript 设置 WebSocket Origin Header?
How to set WebSocket Origin Header from Javascript?
我正在尝试使用 javascript 代表 [= 从本地 test.dev
页面向 IP 123.123.123.123
的服务器 运行 发出 websocket 请求14=]。请求通过,但 123.123.123.123
服务器在 websocket 请求中看到 Origin: test.dev
header 并拒绝连接,因为它想看到 Origin: test.com
.
这里是连接套接字的javascript代码:
ws = new WebSocket("123.123.123.123");
如何使用 javascript 与不诚实的 Origin
header of Origin: test.com
启动 websocket 连接?
我希望这样的东西能奏效,但我找不到这样的东西:
ws = new WebSocket("123.123.123.123", "test.com");
How can I use javascript to start a websocket connection with a dishonest Origin
header of Origin: test.com
?
如果我们可以在 JavaScript 中伪造请求的来源,同源策略就不会很好地保护我们的安全。它的存在只是为了保护我们免受这种和其他潜在的攻击媒介。
由于这看起来像是开发工作,您是否考虑过使用网络调试代理,例如 Fiddler (free) or Charles(付费)?通过这些,您可以为自己的机器或通过调试器代理的任何测试机器修改 WebSocket 的初始握手请求或响应。
简单的解决方案是在 hosts
文件中简单地创建一个条目以将 test.com
映射到 123.123.123.123
。稍后当您想要连接 "real" test.com
.
时,您需要删除此条目
一个不那么棘手的解决方案需要使用代理,它可以 re-write 你的 header 为你 on-the-fly。考虑在您的系统上安装 nginx
,然后将请求代理到 123.123.123.123
,保持一切相同 除了 来源 header。这是您在 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";
}
}
如果你 真的 必须从 javascript 伪造一个假的 'origin' header 值 - 有办法。您不会在普遍接受的原则手册中找到它,但在这里:
创建一个 php 文件来调用套接字,并使用伪造的原始值。现在使用 ajax 调用 php 文件,你 javascript.
它可能不优雅、不合乎道德或不可接受,但永远不要接受任何人告诉你做不到。
'send.php' 是使用 ajax 从 javascript
调用的
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 是一个 class 文件,具有基本的 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;
}
一个好的解决方案是用另一个 websocket 库覆盖 WebSocket 调用,例如 https://github.com/websockets/ws. To use this node.js library in a browser, you just have to use http://browserify.org/。
我正在尝试使用 javascript 代表 [= 从本地 test.dev
页面向 IP 123.123.123.123
的服务器 运行 发出 websocket 请求14=]。请求通过,但 123.123.123.123
服务器在 websocket 请求中看到 Origin: test.dev
header 并拒绝连接,因为它想看到 Origin: test.com
.
这里是连接套接字的javascript代码:
ws = new WebSocket("123.123.123.123");
如何使用 javascript 与不诚实的 Origin
header of Origin: test.com
启动 websocket 连接?
我希望这样的东西能奏效,但我找不到这样的东西:
ws = new WebSocket("123.123.123.123", "test.com");
How can I use javascript to start a websocket connection with a dishonest
Origin
header ofOrigin: test.com
?
如果我们可以在 JavaScript 中伪造请求的来源,同源策略就不会很好地保护我们的安全。它的存在只是为了保护我们免受这种和其他潜在的攻击媒介。
由于这看起来像是开发工作,您是否考虑过使用网络调试代理,例如 Fiddler (free) or Charles(付费)?通过这些,您可以为自己的机器或通过调试器代理的任何测试机器修改 WebSocket 的初始握手请求或响应。
简单的解决方案是在 hosts
文件中简单地创建一个条目以将 test.com
映射到 123.123.123.123
。稍后当您想要连接 "real" test.com
.
一个不那么棘手的解决方案需要使用代理,它可以 re-write 你的 header 为你 on-the-fly。考虑在您的系统上安装 nginx
,然后将请求代理到 123.123.123.123
,保持一切相同 除了 来源 header。这是您在 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";
}
}
如果你 真的 必须从 javascript 伪造一个假的 'origin' header 值 - 有办法。您不会在普遍接受的原则手册中找到它,但在这里:
创建一个 php 文件来调用套接字,并使用伪造的原始值。现在使用 ajax 调用 php 文件,你 javascript.
它可能不优雅、不合乎道德或不可接受,但永远不要接受任何人告诉你做不到。
'send.php' 是使用 ajax 从 javascript
调用的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 是一个 class 文件,具有基本的 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;
}
一个好的解决方案是用另一个 websocket 库覆盖 WebSocket 调用,例如 https://github.com/websockets/ws. To use this node.js library in a browser, you just have to use http://browserify.org/。