Can I interface with Laravel websockets using just JavaScript? - javascript

We currently have a Laravel CMS server that sends a request to another Laravel websocket server which then broadcasts to multiple IoT devices listening on their individual channels for requests. Everything works perfectly.
So it looks like this:
CMS server -> Socket server -> devices
I am trying to initiate these websocket requests from a third server which is not running Laravel, it's just running core PHP and JavaScript. I looked in my browser's Network tab to spy on the websocket connection from a legitimate request, copied it exactly, the connection and subscription succeed but when I send the requests over the socket from my test script, nothing happens. It's literally the exact same socket request over the same channel on the same connection, just with a different socket ID. How could this fail?
I thought maybe it was only accepting requests from that specific server so I copied my test script to that server and still no response. I don't know much about web sockets, could someone please help me understand how I can spoof these messages without using Laravel?
If you're wondering what I'm actually up to, we're decommissioning this CMS server for our new CMS we built in core PHP so we no longer have Laravel as an option but I don't want to rewrite the socket server which uses Laravel, that can stay. I just need to interface with it any way possible. I have full SSH and DNS access to anything I need to configure.
Here is my test script, I've tried about 50 of them I found on Google, none of them give me a response from my specific socket server, besides the connection successful and subscription successful responses.
<script>
let socket = new WebSocket('wss://subdomain.domain.ca:6001/app/apikeyyyyy?protocol=7&client=js&version=4.4.0&flash=false');
var message = {
channel:"aaaaaa.bbbbbbbb",
event:"1000",
data:"{\"channelName\":\"aaaaaa.bbbbbbbb\",\"message\":{\"msgId\":\"\",\"cmd\":\"help\",\"timestamp\":1663616905529,\"request_id\":\"\",\"device_id\":\"\"},event:\"1000\",timestamp:1663616905529,request_id:\"\",device_id:\"\"}"
};
socket.onopen = function(e) {
var data_json = {
event:'pusher:subscribe',
data:{
channel:'aaaaaa.bbbbbbbb',
}
};
socket.send(JSON.stringify(data_json));
socket.send(JSON.stringify(message));
};
socket.onmessage = function(event) {
console.log("Response: " + event.data);
};
socket.onclose = function(event) {
if (event.wasClean) {
alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
alert('[close] Connection died');
}
};
socket.onerror = function(error) {
alert(`[error] ${error.message}`)
};
</script>
I obviously removed the channel name, domain and application key for security but the rest is accurate. And here's what I see in my browser console:
Response: {"event":"pusher:connection_established","data":"{\"socket_id\":\"166323189.236668505\",\"activity_timeout\":30}"}
Response: {"event":"pusher_internal:subscription_succeeded","channel":"aaaaaa.bbbbbbbb"}
When I send a request from the (working) CMS server, I get a OK response from the socket server and the related device will reboot or whatever I asked it to do. When I send the exact same response from my test script, I see the request get logged in the websockets.log file on the socket server but no response is logged like it is with the requests from the CMS server. And no response prints in the browser console even though I do see the responses print in the browser console for my test script when I send them from the CMS so I know the subscription is working correctly. Also the devices do not reboot.

Related

Is it dangerous to establish WebSocket connection in a js file?

So, basically, I have this code :
let socket = new WebSocket('ws://localhost:8080/server.php');
socket.onopen = () => {
console.log('connection established !');
}
I use this code to establish a connection for a Real-Time Quiz. But after going to the Sources page of my inspector, I can see the whole javascript code in my browser, including ws://localhost:8080/server.php. Is it dangerous to show it (unintentionally) ? If someones creates a script and puts in it the same url (not localhost, it's just an example), can he receive/send data to the server ?
yes,it is dangerous. u can:
verify the client http request header for example 'Origin'. make sure the client website is the right client website.
use a TSL websocket service, visit to the server over SSL. So the protocol is changing to: wss://
give the client a request token, put this token in header or in post data, the server verify this token.
check the request times of a client in limited time. make sure a specific client won't request too frequently

Connect to a websites WebSocket server that isn't mine

As the title says I'm trying to connect to a website their WebSocket to get live updates. However I've tried everything, and all it does is the WebSocket immediately disconnects after connecting. I've checked their version of the connection to the WebSocket, they do not reply with anything after the onopen event. I've tried setting the origin in case of them not accepting any connections from origins other than their own.
I'm trying to do this within NodeJS with the ws module. I'm trying to reach the WebSocket of https://rollbit.com (wss://ws.rollbit.com/);
Code
const WebSocket = require('ws');
const ws = new WebSocket('wss://ws.rollbit.com/', {
origin: 'https://rollbit.com'
});
ws.on('open', function open() {
console.log('connected');
});
ws.on('close', function close() {
console.log('disconnected');
});
I've also attempted to do this from within the browser, it works perfectly when I do it from the console when being on their site, when changing to another site and running the code there - it fails. I've tried cookies as well..
Result
connected
disconnected
Any ideas of what I'm missing here?

NodeJS and web sockets: Check if socket origin is the same as the web socket server

Edit: I'm sending data from app A to app B over web socket, where A is a router app for something else, and B is my web app. When B receives the data, it should send it to any client viewing its home page, also over web socket. But, since the roter app and the home page clients are connected to the same web socket server, I don't know which connections are to the clients viewing the home page, and which connections are to other stuff, like my router. Only the home page clients should receive the data.
I basically want to pass the logging data recieved from my router to my home page in real time so I can view it.
========
I have an express app that server a simple html page. It runs this script:
var host = window.document.location.host.replace(/:.*/, '');
var ws = new WebSocket('ws://' + host + ':5000');
ws.onmessage = function (event) {
console.log(JSON.parse(event.data));
};
In the nodejs backend, I have a simple ws server running, listening for connections:
var WebSocketServer = require("ws").Server;
module.exports.init = function(server) {
var wss = new WebSocketServer({ server: server });
wss.on('connection', function (ws) {
...
});
};
I get connections from, at the moment, 2 different locations.
A router app I have running that is sending this web app logging messages.
The html-page that this web app is serving.
I want to pipe the data from the router app to my html page, but to do that I need to know which of my connections I need to pipe the data to. I can in theory have many connection, but only one of them, at least for now, should be passed the data after it is recieved.
I thought I could compare the origin of the web socket connection to the domain of the web server the web socket server ran on.
I can get the origin of the connection like this: ws.upgradeReq.headers.origin. That will return e.g: localhost:5000. But I don't know the domain name where my web socket server is running. I've tried to google, and it seems like to get the domain name, I need to get it from an http request. What I am looking for is something that just gives me the name, without having to wait for an http request.
I've tried os.hostname(), but it doesn't give me the results I need.
I've also tried server.address(), where server is var server = require("http").createServer(app);, but that gives me this: { address: '::', family: 'IPv6', port: 5000 }.
Isn't there just a way to get the host and port? Can I somehow use the address part above to get the host name?
The web app will probably run on Heroku.
Based on your recent comments, it sounds like each browser client that connects a webSocket should just tell your server what web page it is looking at with an initial message and the server should keep track of that for each active connection.
In socket.io (built on top of webSockets), you could just connect to the /homepage namespace and then that server could broadcast to all sockets connected to that namespace. You could, of course, implement that type of functionality yourself with a plain webSocket.
Then, your server would not only have a list of connected sockets, but could also know what page they were all from. That would allow you to broadcast based on current page. Your server-to-server webSocket would not have sent a message that it's from the home page, so it would not be tagged as such and you could avoid sending to it.
You might find socket.io easier to use for all of this. In additon to namespaces on both client and server, it also gives you automatic reconnection from browsers, a simpler message passing system, server-side broadcast to namsepaces and so on.

How to read status code from rejected WebSocket opening handshake with JavaScript?

I created a WebSocket client in JavaScript
if ("WebSocket" in window) {
ws = new WebSocket(url);
ws.binaryType = "arraybuffer";
} else if ("MozWebSocket" in window) {
ws = new MozWebSocket(url);
ws.binaryType = "arraybuffer";
}
and a WebSocket server application. For certain cases I programmed the server to reject the connection request and provide an error code.
In e.g. Firefox Console then a message is shown
Firefox can't establish a connection to the server at ws://123.123.123.123:1234/.
and it provides the status code
HTTP/1.1 403
which is the error code that I have sent by my WebSocket server.
My question is: how can I read this status code in my JavaScript client?
ws.onerror = function(e) {
console.log(e);
};
ws.onclose = function(e) {
console.log(e);
};
are both called, but none of the Event objects contains this error code.
The spec forbids reading the HTTP status code (or anything like it) from the WebSocket object because otherwise the WebSocket object could be used to probe non-WebSocket endpoints, which would be a security issue:
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:
A server whose host name could not be resolved.
A server to which packets could not successfully be routed.
A server that refused the connection on the specified port.
A server that failed to correctly perform a TLS handshake (e.g., the server certificate can't be verified).
A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).
A WebSocket server that sent a correct opening handshake, but that specified options that caused the client to drop the connection (e.g. the server specified a subprotocol that the client did not offer).
A WebSocket server that abruptly closed the connection after successfully completing the opening handshake.
— https://www.w3.org/TR/websockets/#feedback-from-the-protocol
There is another way to do it though!
The WebSocket protocol allows for custom close codes:
4000-4999
Status codes in the range 4000-4999 are reserved for private use and thus can't be registered. Such codes can be used by prior agreements between WebSocket applications. The interpretation of these codes is undefined by this protocol.
— https://www.rfc-editor.org/rfc/rfc6455#section-7.4.2
In your server-side logic, even when you ultimately want to reject the connection (like say the user is currently unauthenticated), do this instead:
Accept the WebSocket connection
Immediately close the connection with a custom close status
The client can now look at the CloseEvent.code to know why the connection was rejected.
You don't need to do this every time the server wants to reject a WebSocket connection. For example, I'd still reject the connection with a 4xx HTTP status if the request isn't a proper WebSocket request, or for security reasons (like if the anti-CSWSH Origin check fails). You only need to use the WebSocket close status for cases that you want the client-side logic to handle.

node.js http server, detect when clients disconnect

I am using express with node.js for a http server. I store the response object so I can stream events down to the client on that channel. Is there a way to detect when the client disconnects? When I kill my client I can still write to the response object without getting any kind of exception/error. It looks like the service keeps the underlying tcp channel open as long as I keep writing to the response object.
req.on("close", function() {
// request closed unexpectedly
});
req.on("end", function() {
// request ended normally
});

Categories