Preventing additional connections in the socket io - node.js

How to only allow access to connections from the app in nodejs and socketio?

You will want to pass a JWT with the connection like
socket = io.connect(server, {
"transports": ['websocket'],
query: {
token: JWTTOKEN,
},
"forceNew":true
});
On your socket server you decode the token, if not then don't let them connect. The client request the token each time then its passed to the server for connection.

Related

Get socket handshake data before proxying websocket request in nodejs via http-proxy

I want to access the handshake data that socket.io client has sent along with the handshake request:
socket=io(origin, {
transport:["polling", "websocket"],
auth: {
token: 'abcd',
id: 'someid',
}
});
Now in http-proxy, I want to access this data before proxying the request to the target. How can I achieve this? I have to access the user information before the upgrade and at arrival of handshake data.
server.on('upgrade', function (req, socket, head) {
// access auth data here
proxy.ws(req, socket, head);
});

Prevent external users to connect websocket (wss)

I'm building a react-nodejs website at https://example.com and it utilizes websocket (using socket.io library), but right now people can just npm install socket.io-client and connect to my websocket using this:
const {io} = require("socket.io-client");
const socket = io("wss://example.com");
Now they can emit/listen to my backend. At the moment, I use ReCAPTCHA to prevent bots so you need to pass a token to connect to the socket.io server, I was wondering whether there is a better solution than this.
You are correct, you can't prevent people from trying to connect to your socket.io server manually with a custom script using socket.io-client. Normally that's a reason for you to design the server in a way, that connected client sockets can't do any harm. Client sockets are usually on the receiving side.
But of course there is a best-practice on how to authenticate the connecting clients, and, if the authentication is failing, to prevent the connection. Checkout out the socket.io Documentation about client options. There is an auth field available :
Client
import { io } from "socket.io-client";
const socket = io({
auth: {
token: "abcd"
}
});
Server
io.on("connection", (socket) => {
console.log(socket.handshake.auth); // prints { token: "abcd" }
});
So once the user is logged in, use the JWT (Token) (assuming you having a JWT Authentication in place) and pass it during establishing the connection. Then validate the JWT on the server side.
client-certificate authentication
There is also a more advanced option for client authentication which uses certificates:
Client
const fs = require("fs");
const socket = require("socket.io-client")("https://example.com", {
ca: fs.readFileSync("./server-cert.pem"),
cert: fs.readFileSync("./client-cert.pem"),
key: fs.readFileSync("./client-key.pem"),
});
Server
const fs = require("fs");
const server = require("https").createServer({
cert: fs.readFileSync("./server-cert.pem"),
key: fs.readFileSync("./server-key.pem"),
requestCert: true,
ca: [
fs.readFileSync('client-cert.pem')
]
});
const io = require("socket.io")(server);

socket io getting peer client certificate always returns empty object

I need to check every peer client certificate through sockets, so I am using socket.io with NodeJS and here's what I did in the server to fetch the certificate:
Server Side
io.on('connection', function(socket) {
let cert = socket.client.request.client.getPeerCertificate();
});
Client Side
const io = require('socket.io-client'),
ioClient = io.connect(
'https://myawesomesockets.com',
{
secure: true
}
);
So the cert object is empty whenever I am trying to connect in the client. Is there something I am missing ? Thanks in advance !

Socket.io client : Websocket Connection failed : Connection closed before receiving a handshake response

I'm building an web app(nodejs, port: xxx0) which interacts with an another app(nodejs, port:xxx5) which is serving as the socket.io server. My socket.io client code goes well in my web app. All works well in my local as the connections refers to the server as io.connect('http://localhost:xxx5/')
But the same is not working when i promote it to higher env as the connection string is as io.connect('https://domainName/')
When my app is trying to connect, I'm receiving the below error:
websocket.js:112 WebSocket connection to 'wss://domainName/socket.io/?EIO=3&transport=websocket' failed: Connection closed before receiving a handshake response
Am I missing anything?
When I try hitting the server(higher env) via socket.io tester, I get the response as No sockets Found and immediately then An error occurred while reconnecting
NOTE:. The server instances are registered in an api gateway in higher env.
Server.js
const app = require('express')()
const port = process.env.PORT || xxx5
const server = app.listen(port)
const io = require('socket.io').listen(server)
let ns = io.of('/namespace')
ns.on('connection', (socket) => {
logger.info('User connected')
socket.emit('messages', {new:2,[{title:'Hi'},{title:'Test'}]})
})
Client.js
import io from 'socket.io-client'
const socket = io(`https://domainName/namespace`, { transports:
['websocket'], rejectUnauthorized: true})
// const socket = io('http://localhost:xxx5/namespace', { transports:
['websocket'], rejectUnauthorized: true})
// above commented url is in local
socket.on('connect', () => {
logger.info("Connected")
})
socket.on('messages', (data) => {
logger.info(`New messages : ${data.new}`)
})
socket.on('error', (error) => {
logger.error(`Error encountered : ${error}`)
})
Is your application running as a priveleged user? Any TCP/UDP socket less than 1024 is considered and IANA reserved port and required elevated priveleges to bind as a listening service.
Using cURL can also aid in providing the necessary handshake: (See reference of websocket handshake process here; https://en.m.wikipedia.org/wiki/WebSocket#Protocol_handshake)
sh
$ curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: echo.websocket.org" -H "Origin: http://domainname " http://domainname

Setting socket.io client 'resource' for nginx reverse proxying path (no websockets)

I have a socket.io server only using the 'xhr-polling' transport running at port 5000 on a system with nginx configured to pass requests that start with '/api' to that port.
In my client, on connect, I specify the socket.io resource as 'api/socket.io' which appears to correctly forward the initial connection request to the server and trigger a connection event. However, responses sent from the server using socket.emit do not reach the client.
If I connect the client directly to port 5000, leave off the 'resource' option, and bypass nginx forwarding, everything seems to work ok.
Server:
var io = require('socket.io').listen(5000, {transports: ['xhr-polling']});
io.sockets.on('connection', function (socket) {
console.log('Connection spotted!');
socket.emit('connected', 'hi');
});
Client (not working):
var socket = io.connect(window.location.hostname, {resource:'api/socket.io'});
socket.on('connected', function(data) {
alert('Connected!!!');
});
Client (working):
var socket = io.connect(window.location.hostname, {port: 5000});
socket.on('connected', function(data) {
alert('Connected!!!');
});
nginx:
location / {
proxy_pass http://localhost:5000/;
}
In both cases I see the "Connection spotted!" log message on the server, but client gets data and alerts when connected directly to the port.

Resources