block access from subdomains in socket.io - javascript

I'm trying to allow access to socket.io only if the website the connection is coming from is one of the whitelisted subdomains on my server. The best would be if I could check the origin subdomain everytime a client connects to my socket.io server. I tried finding out how to do it, but haven't found a good solution yet.
The only thing that comes close to a solution is this answer to a related question. However - I'm not sure if that's the best way to do it and if that even works in my case and can't be faked via javascript.
TLDR: How do I treat socket.io requests differently based on their origin? If that's not possible: How do I host two socket.io servers on two subdomains, but same port?
Regarding duplicate flag: My Question is entirely different. I cannot use namespaces as a solution since I can't trust the client side javascript running on some subdomains. Therefore these subdomains could just join a different namespace, which would make my efforts to separate them pointless.

I found this answer with the help of some guy on the socket.io slack server.
io.on('connection', function (socket) {
var subdomain = socket.handshake.headers.host.split('.')[0];
if (subdomain === 'allowed') {
socket.on('login', /* ... */);
} else {
socket.on('login', function () {
console.log('login disabled, wrong subdomain');
});
}
}
I don't know it it's reliable or can be modified by malicious client javascript, but it was looking quite good while I was testing it.

I added the this modifie code to the express/socket.io middleware function so it gets called on every request: connect, disconnect, and streaming.
I also use the express-subdomain npm
https://www.npmjs.com/package/express-subdomain
app.sio.use((socket, next) => {
var subdomain = socket.request.headers.host
sessionMiddleware(socket.request, {}, next)
})

Related

Keeping Web Socket Server Alive

Zup coders. I've implemented a simple website that uses Web Sockets PHP (Consik Yii2 solution: https://github.com/consik/yii2-websocket) vs JS (Html5).
Everything is working fine, I only have one issue with my solution, making sure the server is always alive.
I though about saving the WebSocket Instance into Cache and throw a cron that checks the state of the instance. I installed memcached and found out that i can´t save a serialized version of the WebSocket Server instance. ¿Is this a good solution? ¿Would Redis Caché fix this?
I also thought about using client side JS to react to "Error during WebSocket handshake: Unexpected response code: 200" but i can't seem to get it working. I also don't like making the URL that starts websockets public.
Ex:
connect = function(){
websocket = new WebSocket(webSocketURL);
websocket.onerror = function(){
$.get( "/startWebSocketServer",
function(data){
connect();
}
);
};
};
connect();
Thanks!
I think that as matter of fact you need a process supervisor who takes care to "supervise" your server process and do actions in response of process/system events like crash, restart etc..
There are several solutions for each case (standard OS implementations, personal preferences, fit your need), here a list http://without-systemd.org/wiki/index.php/Init , Service managers section could best fit your needs.
Supervisord is easy to setup and configure, it could be a good start thanks to a good bunch of examples around the net.
Solution 1: using a cache could not be the most orthodox way to implement a custom-made supervisor.
Solution 2: is legit as long as it informs user about a problem, the call to an exposed endpoint to start a service IMHO could be a security flaw.

Websockets not connected behind proxy

This is quite common problem, but I cannot find a solution to my specific case. I'm using Glassfish 4.1.1 and my application implements Websockets.
On a client side I'm connecting to WS-server simply by:
var serviceLocation = "ws://" + window.location.host + window.location.pathname + "dialog/";
var wsocket = new WebSocket(serviceLocation + token_var);
On a server side websockets are implemented via #ServerEndpoint functionality and looks very common:
#ServerEndpoint(value = "/dialog/{token}", decoders = DialogMessageDecoder.class)
public class DialogWebsoketEndpoint {
#OnOpen
public void open(final Session session, #PathParam("token") final String token) { ... }
etc.
}
Everything works fine up to the moment when customer tries to connect behind proxy.
Using this test: http://websocketstest.com/ I've found that computer of the customer works behind http-proxy 1.1.
He cannot connect to websockets, onopen simply do not fire at all. wsoscket.readyState never become 1.
How can I tune my ServerEndpoint to make this code work even when customer is connecting behind proxy?
Thank you in advance!
UPDATE: I would provide a screenshot with websocketstest at that computer:
On my computer it seems similarly except one thing:
HTTP Proxy: NO.
Much as the comments to the questions state, it seems the Proxy doesn't support Websockets properly.
This is a common issue (some cell-phone companies have proxies that disrupt websocket connections) and the solution is to use TLS/SSL connections.
The issue comes up mainly because some proxies "correct" (read: corrupt) the Websocket request headers.
However, when using TLS/SSL, the proxies can't read the header data (which is encrypted), causing data "pass-through" on most proxies.
This means the headers will arrive safely at the other end and the proxy will (mostly) ignore the connection... this might still cause an issue where connection timeouts are concerned, but it usually resolves the issue.
EDIT
Notice that the browsers will protect the client from mixing non-encrypted content with encrypted content. Make sure the script initiates the ws connections using the wss variant when TLS/SSL connections are used.

Determine if Request is Local in Node.js app

I am learning Node.js. While creating a web site, I will run the web site locally (on localhost). When it is deployed, it will run on other servers. My question is, how do I determine if a request is from localhost or not in Node? In ASP.NET, I could use Request.IsLocal. I'm trying to figure out how to do that in Node.
Thank you!
There's server.address() to get the server address.
And request has connection and socket objects, as both might hold remote address (in a remoteAddress property) depending on a type of current connection.
But if the server is behind a reverse proxy, you'll have to pull it from appropriate header, most likely x-forwarded-for. However I'm not sure if that holds if proxies are chained.
So, to conclude, you'd do something along the lines of:
function cliAddress(req) {
return req.connection.remoteAddress || req.socket.remoteAddress || req.headers['x-forwarded-for'];
}
server.isLocal = function(request) {
return server.address() === cliAddress(req);
}
And if you use express.js see Express.js Req.IP API.

Express.js: reverse proxying different web app along with assets

I want to allow an authenticated client in Express to access to other web applications that are running on the server, but on different ports.
For example, I have express running on http://myDomain and I have another application running on say port 9000. I want to be able to reach the other app through http://myDomain/proxy/9000.
I had a little bit of success using node-http-proxy, for example:
function(req, res) {
var stripped = req.url.split('/proxy')[1];
var path = stripped.split('/');
var port = path.shift();
var url = path.join('/');
req.url = url;
proxy.web(req, res, {
target: 'http://127.0.0.1:' + port
});
}
However, the big problem is that when the web app makes GET requests, such as for /js/lib.js, it resolves to http://myDomain/js/lib.js, which is problematic because express is not aware of those assets. The correct request would be to http://myDomain/proxy/9000/js/lib.js. How do I route all these additional requests?
What you need to do is to replace URLs in the initial page with the new URL pattern. What is happening is that the initial page that your reverse proxy returns has a reference to:
/js/lib.js or http://myDomain/js/lib.js
so when the browser makes a second request it has the wrong pattern for your reverse proxy.
Based on the incoming request you know what the pattern should look like. In your example it's http://myDomain/proxy/9000. You then fetch the appropriate page from the other server running on http://127.0.0.1:9000/. You do a string replace on any resources in that file. You'll need to experiment with the pattern but you might look for 'script src="/' or 'href="/' and you might find regex helps with the pattern if, for example, the src attribute isn't the first listed in a script tag.
For example you might find 'scr="/' and then you replace it with 'src="/proxy/9000/' that way when the browser asks for that local resource it will come through with the port that you're looking for. This is going to need experimentation and it's a great algorithm to write unit testing around to get perfect.
Once you've done the replacement you just stream that page to the client. res.send() will do this for you.
Something else that you might find useful is that ExpressJS gives you a way to pull out the port number with a little less hassle than you're doing. Take a look at this example:
app.get('/proxy/:port', function(req, res){
console.log('port is ' + req.params.port);
});
I don't think http://myDomain/proxy/9000 is the correct way to do it. Web pages are going to assume the site's domain to be just myDomain and not myDomain/proxy/9000, because that is what the standard says.
Your use case would be better served by using subdomains like 9000.proxy.myDomain.

websocket client discovering server

I am in a situation where I want my websocket client to connect to server but server ip or dns name is unknown. Both client and server are in local network(connected to same router). I tried something like this....
var socket;
for(var i=1; i<255; i++) {
socket = new WebSocket('ws://192.168.1.'+i+':8080/service');
socket.onopen = function () {
console.log('WebSocket Connected!!');
};
socket.onclose = function (event) {
console.log('WebSocket Disconnected!!');
socket.close();
};
socket.onmessage = function (event) {
console.log('WebSocket receive msg: ' + event.data);
}
}
This works but I am not sure if I am doing it right or if there is a better way to do it. Any help is appreciated.
Have you tried hooking up an onerror listener to see what errors are being thrown? It's possible that you're finding the server but that some mis-configuration in the server is causing it to error out before the connection is opened.
WebSockets is still a very actively evolving standard. There are multiple drafts out there and some browsers support some drafts but not others. Old drafts are often considered insecure, so some browsers don't support them, while other browsers only support the old drafts because they haven't been updated for the newer ones. Also, servers may be in the same boat. It's kind of the wild west.
I suggest putting in robust error handling and also fallback when things aren't working right. Packages like Socket.io offer this kind of transparent fallback support. I suggest checking that out if you're looking for a quick solution. However, if you're just using this as a learning experience (and I encourage such behavior!), you will want to hook up an onerror handler to see what's going on and why each connection fails.
This solution would of course scale horribly when you deploy on a network larger than class A. And when there is more than one websocket server on the network, you wouldn't know which one to hit.
But as long as there is no DNS and no static IP addresses on your network, port-scanning the whole IP range is the only way to find the server.
Are you sure it's impossible to assign a static IP address to the server machine? Most consumer grade routers don't strictly enforce the use of DHCP and usually it's not a problem when only some machines are configured with static IP addresses. Some router firmwares also allow to configure the DHCP server to always assign the same IP address to specific MAC addresses.

Categories