I'm using https://github.com/lemmingzshadow/php-websocket/
I can allow some domains, and I have allowed localhost
and a domain which points to my local server. But I wonder if someone else which has a server on his computer can connect to my websocket (through my domain) using an script in his localhost server.
Here is the relevant code:
-> server/server.php
$server->setAllowedOrigin('localhost');
$server->setAllowedOrigin('mydomain.com');
-> server/lib/WebSocket/Connection.php
// check origin:
if($this->server->getCheckOrigin() === true)
{
$origin = (isset($headers['Sec-WebSocket-Origin'])) ? $headers['Sec-WebSocket-Origin'] : false;
$origin = (isset($headers['Origin'])) ? $headers['Origin'] : $origin;
if($origin === false)
{
$this->log('No origin provided.');
$this->sendHttpResponse(401);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
if(empty($origin))
{
$this->log('Empty origin provided.');
$this->sendHttpResponse(401);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
if($this->server->checkOrigin($origin) === false)
{
$this->log('Invalid origin provided.');
$this->sendHttpResponse(401);
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
$this->server->removeClientOnError($this);
return false;
}
}
-> server/lib/WebSocket/Server.php
public function checkOrigin($domain)
{
$domain = str_replace('http://', '', $domain);
$domain = str_replace('https://', '', $domain);
$domain = str_replace('www.', '', $domain);
$domain = str_replace('/', '', $domain);
return isset($this->_allowedOrigins[$domain]);
}
public function setAllowedOrigin($domain)
{
$domain = str_replace('http://', '', $domain);
$domain = str_replace('www.', '', $domain);
$domain = (strpos($domain, '/') !== false) ? substr($domain, 0, strpos($domain, '/')) : $domain;
if(empty($domain))
{
return false;
}
$this->_allowedOrigins[$domain] = true;
return true;
}
Edit:
Maybe I wasn't clear enough. I want that everybody can connect to the websocket but only if they are at my domain (or my localhost), something like Same Origin Policy in AJAX.
My worry is that if I allow localhost, maybe all other localhost in other computers will be allowed too.
In fact, the Cross-Site WebSocket Hijacking attack is possible when the WebSocket handshake is vulnerable to CSRF. Indeed, the communication channel between the two parties (client/server) is created according to the origin of the opening request.
You should strongly prefer the secure wss:// protocol over the insecure ws:// transport. Like HTTPS, WSS (WebSockets over SSL/TLS) is encrypted, thus protecting against man-in-the-middle attacks. A variety of attacks against WebSockets become impossible if the transport is secured.
WSS is secure, so it prevents things like man-in-the-middle attacks. A secure transport prevents many attacks from the start. In conclusion, WebSockets aren't your standard socket implementation. WebSockets are versatile, the established connection is always open, and messages can be sent and received continuously.
Some WebSockets security vulnerabilities arise when an attacker makes a cross-domain WebSocket connection from a web site that the attacker controls. This is known as a cross-site WebSocket hijacking attack, and it involves exploiting a cross-site request forgery (CSRF) vulnerability on a WebSocket handshake.
If you want to be sure, you should add an IP check, analyzing $_SERVER["REMOTE_ADDR"]
. The origin that is being checked is a text value provided by the client which can easily be forged.
if (!in_array($_SERVER["REMOTE_ADDR"], array("127.0.0.1", "ip.of.dom.ain")) exit;
If you don't want to hardcode the IP address or if that changes frequently, you can use gethostbyname to fetch the IP address for the domain like this. Be aware that this will add a delay to each request when the DNS must be queried for the domain(s) and this way will cause timeouts when your DNS goes down. (the following code could be optimized, of course)
$allowed_origins = array('localhost', 'mydomain.com');
$allowed_ips = array();
foreach ($allowed_origins as $domain) {
$server->setAllowedOrigin($domain);
$allowed_ips[] = gethostbyname($domain);
}
if (!in_array($_SERVER["REMOTE_ADDR"], $allowed_ips)) exit;
Probably the proper way to do this would be to protect your resource through your web server so that it only accepts requests from the IP addresses you wish to allow (see deny in Apache or deny in nginx).
Although I was worried about allowing connections from all localhost when I wanted to allow only my localhost, i have found that in another question, kanaka said:
To clarify, Javascript running in an uncompromised and well-behaved browser cannot affect the value of Sec-WebSocket-Origin which holds the value of the original site the page was loaded from (allowing your server to only allow certain origination points). However, if you had Javascript running in Node.js as a WebSocket client, it could set the origin value to whatever it wants. The CORS security is for the safety of browser users, not for your server. You need other mechanisms to protect your server.
Then, my origin checking code is not safe at all; and it doesn't matter if all allow all localhosts because if someone has enough knowledge for connecting to my websocket using his own localhost, it is likely that he could modify the Origin header.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With