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

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

Related

Can I interface with Laravel websockets using just 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.

Connect client socket to a cookie

I am making a chat program.
I am using an Nginx server and NodeJS.
I have setup a websocket via ssl and that works fine.
I have decided to use cookies for authentication.
There are two functions which are crucial:
mconnection.prototype.make_server_https=function(){
console.log('Make server https');
var cthis=this;
var server_https=modules.https.createServer({
key: this.ssl_key,
cert:this.ssl_cert,
ca:this.ssl_ca
},(request,response)=>{
console.log('### CreateServer ###');
console.log('CreateServer, Request:');
console.log(request);
console.log('CreateServer, Response:');
console.log(response);
console.log('######');
and
mconnection.prototype.make_server_websocket=function(){
var server_websocket=new modules.ws.Server({server:this.server_https});
var cookie = require("cookie");
var cthis=this;
//whenever a new client connects with the server.
server_websocket.on('connection', function(client_socket, request){
console.log('### On Connection ###');
console.log('OnConnection, Client Socket:');
console.log(client_socket);
console.log('OnConnection, Request:');
console.log(request);
console.log('######');
If I do state the port number in the client url,function make_server_https gets run and inside there i can access the cookie and set it via the response object.
but in the original url,function make_server_websocket gets run, and there i have access to the client_socket on the server. But there it seems i dont have access to the cookies.
I need to client_websocket to start the connection with this given client. And I need to tie it somehow with the cookies login information.
But i never have both at the same time so i dont get how i could connect them to make the login happen.
I am probably misunderstanding something, any help in the right direction would really be appreciated.
you have to serve you index page from node server using GET then when the request reaches backend you will have response object which can then be used to SET-COOKIE if not set from backend.
And after GET request is complete COOKIE will be added in browser, when next request is made for websocket connection COOKIE will be added to the request in REQUEST HEADERS by the browser which will be available in backend through request object.
And if you decide to use it in login system then you can SET-COOKIE on successfull login.
i got it. its an event called on headers, not on connection. and there i can just push onto the headers.

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.

websocket reconnect using session id

I'm using Java and Websocket API for my application
This is the problem I face :
I open a websocket connection to connect through certain server :
ws = new WebSocket(targetURL);
When I send every message using ws, I always check the state of the ws first, by :
if(ws.readyState == 1) {
ws.send(request);
} else {
alert("THE CONNECTION IS CLOSED. PLEASE REFRESH YOUR BROWSER.");
}
I saved an information in my EndPoint during the communication between client and server. But sometimes, when I want to send message from client, the state is not 1, which means that the ws is no longer open (CMIIW). If I try to connect again using ws = new WebSocket(targetURL), it will create a new connection, thus the information in my EndPoint become lost.
So my question is :
Is there any way to reconnect a closed connection of Websocket using Session ID or any other unique ID so that the information in my EndPoint can be preserved?
Thank you
WebSockets use a HTTP handshake, and it will send whatever cookie they have for that origin. So a way of doing it, is assign a cookie to you clients, and they will forward it on connection. You could even set the cookie in the handshake response, if the framework you are using it allows it. You can use that cookie on connection to figure out the ID or assign a new ID if no cookie.
Other option is to create for example your own "hello" protocol, where the browser has to send a special command before start, indicating its ID if any, or just an null ID if it is first time.
But aside of the cookie, there is not built-in mechanism for that.

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.

Categories