Intercept (and potentially deny) web socket upgrade request - node.js

I have a Node.js server that I am sending a web socket upgrade request to. The Authorization header of this request contains login information, which I need to compare against a database entry. I'm unsure how I can stop the web socket connection from opening until after my database query callback is executed.
The following is a simplification of what I am currently doing:
var Express = require('express')
var app = Express()
server = app.listen(app.get("port"), function () {})
server.on("upgrade", function (request, socket) {
//Query database
//On success set "authenticated" flag on request (later accessed through socket.upgradeReq)
//On failure abort connection
})
This works, but there is a brief period of time where the socket is open but I haven't verified the Authorization header, so it would be possible for a malicious user to send/receive data. I'm mitigating this risk in my implementation through the use of an "authenticated" flag, but it seems like there must be a better way.
I tried the following things, but while they seemed to intercept all requests except the upgrade ones:
Attempt #1:
app.use(function (request, response, next) {
//Query database, only call next if authenticated
next()
})
Attempt #2:
app.all("*", function (request, response, next) {
//Query database, only call next if authenticated
next()
})
Possibly worth noting:
I do have an HTTP server as well, it uses the same port and accepts POST requests for registration and login.
Thank you for any assistance, please let me know if additional information is needed.

I'm not sure if this is correct HTTP protocol communication but it seems to be working in my case:
server.on('upgrade', function (req, socket, head) {
var validationResult = validateCookie(req.headers.cookie);
if (validationResult) {
//...
} else {
socket.write('HTTP/1.1 401 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'\r\n');
socket.close();
socket.destroy();
return;
}
//...
});

verifyClient is implemented for this purpose!
const WebSocketServer = require('ws').Server
const ws = new WebSocketServer({
verifyClient: (info, cb) => {
const token = info.req.headers.token
if (!token)
cb(false, 401, 'Unauthorized')
else {
jwt.verify(token, 'secret-key', (err, decoded) => {
if (err) {
cb(false, 401, 'Unauthorized')
} else {
info.req.user = decoded
cb(true)
}
})
}
}
})
src:
Websocket authentication in Node.js using JWT and WS

Related

Nuxt.js 2 - how to access hook instance in server middleware

Let me preface this by saying that this is an area of Nuxt I have literally zero experience in and there seems to be very few resources about it online, even Nuxt's docs are lacking. So feel free to correct any incorrect assumptions I make in this post.
I'm running a websocket listener in a hook in a module and a regular Express api server in a server-middleware. On their own they seem to be working without issue, but what I can't seem to get right is accessing the websocket listener in one of the api endpoints, so that if the api gets hit I can notify the clients subscribed to the ws. Let me show you.
Here's my websocket listener hook module:
// #/modules/ws.js
const WebSocket = require('ws')
const wss = new WebSocket.Server({ noServer: true })
wss.on('connection', ws => {
console.log('client connected', wss.clients.size)
ws.on('message', data => {
console.log('received:', data)
data = JSON.parse(data)
if(data.type === 'connection') {
ws.clientId = data.id
return ws.send(JSON.stringify({ type: 'connection', status: 'success', id: ws.clientId }))
}
})
})
export default function () {
this.nuxt.hook('listen', server => {
server.on('upgrade', (request, socket, head) => {
wss.handleUpgrade(request, socket, head, ws => {
wss.emit('connection', ws)
})
})
})
}
And then here's my api server:
// #/server-middleware/index.js
const express = require('express')
const app = express()
app.post('/test-route', (req, res) => {
console.log('connection received')
// here I'd now like to access wss to notify subscribed clients
res.json({ status: 'success' })
})
module.exports = app
And then wiring it all up in nuxt.config.js
...
modules: ['#/modules/ws'],
serverMiddleware: ['#/server-middleware/index'],
...
Firstly, I'm worried that I'm going about this all the wrong way and that I should somehow be sticking the api server into modules/ws.js then everything should be accessible right there, but I have no idea how to take over Express, such that it still works, so if you have advice here, I'd super appreciate it.
Short of that, I'd just like to access the wss object that was instantiated in modules/ws.js in server-middleware/index.js because then theoretically I should still be able to push messages to subscribed clients, right? But how do I do that? I tried straight requireing the exported default function from ws.js, but that seems to just return the function and not the wss instance. I also tried looking at the this.nuxt object in the middleware, same as in the hook module, but that just returns undefined.
So now I'm out of things to try, and with how foreign all of this is to me, I can't even conceive of anything else that might be remotely related. Any advice about any of this will be greatly appreciated.
Nuxt.js version 2.15.7, FWIW
One possible way is to separate the WebSocket server into a different file to import for later use.
Example:
// #/modules/ws/wss.js
const WebSocket = require('ws')
const wss = new WebSocket.Server({ noServer: true })
wss.on('connection', ws => {
...
})
module.exports = {
handleUpgrade (request, socket, head) {
wss.handleUpgrade(request, socket, head, ws => {
wss.emit('connection', ws)
})
},
getActiveClients () {
return [...wss.clients].filter(
client => client.readyState === WebSocket.OPEN
)
}
}
// #/modules/ws/index.js
const wss = require('./wss')
export default function () {
this.nuxt.hook('listen', server => {
server.on('upgrade', wss.handleUpgrade)
})
}
Then use it in your server middleware:
// #/server-middleware/index.js
const express = require('express')
const wss = require('../modules/ws/wss')
const app = express()
app.get('/broadcast', (req, res) => {
for (let client of wss.getActiveClients()) {
client.send('broadcast!')
}
res.json({ status: 'success' })
})
module.exports = app

Authenticate jwt token and proxy to another server using Node JS

Want to use NodeJS as reverse proxy to another server.
My Scenario is to authenticate all requests (each request will have jwt token), authentication using jwt in nodejs is fine.
Same request should be sent to another server after successful authentication and the response should be sent back to client.
Looked into redbird, node-http-proxy and other readily available nodejs proxy modules. Nothing have a concrete way of authenticating the jwt and redirecting to target.
Is there a module which can be used? If not any idea/what steps I can follow to achieve this? Also I will be adding tls termination.
I was able to get something to work this might not be an optimal solution. This is an http proxy server. Next quest is to modify this to use as https server with tls termination (which is not the scope for this question atleast)
let http = require('http')
let httpProxy = require('http-proxy')
let jwt = require('jsonwebtoken')
let token = require('./token.json')
let proxy = httpProxy.createProxyServer({})
let server = http.createServer(function(req, res) {
jwt.verify(req.headers["authorization"], token.secret, { issuer:'IssuerName' }, function(err, decoded) {
if(err){
console.log("Error: " + err)
res.setHeader('Content-Type','application/json')
res.write(JSON.stringify({"statusCode":401, "message":err}))
res.end()
return
}
proxy.web(req, res, {
changeOrigin: true,
target: 'https://some_url'
})
})
})
proxy.on('proxyReq', function(proxyReq, req, res, options) {
proxyReq.removeHeader('authorization')
})
proxy.on('error', function (err, req, res) {
res.setHeader('Content-Type','application/json')
res.write(JSON.stringify({"statusCode":500, "message":err}))
res.end()
})
let port = process.env.PORT || 9200
server.listen(port)
console.log('HTTP Proxy server is running on port: ' + port)

How to get spark instance on using Primus middleware

I have setup a Primus websocket service as below.
http = require('http');
server = http.createServer();
Primus = require('primus');
primus = new Primus(server, {
transformer: 'websockets',
pathname: 'ws'
});
primus.on('connection', function connection(spark) {
console.log("client has connected");
spark.write("Herro Client, I am Server");
spark.on('data', function(data) {
console.log('PRINTED FROM SERVER:', data);
spark.write('receive '+data)
});
spark.on('error', function(data) {
console.log('PRINTED FROM SERVER:', data);
spark.write('receive '+data)
});
});
server.listen(5431);
console.log("Server has started listening");
It works fine. In above code, I use spark.write to send response message to users. Now I want to convert it to be used in a middleware.
The code becomes as below:
primus.use('name', function (req, res, next) {
doStuff();
});
in the doStuff() method, how I can get the spark instance to send message back to clients?
The readme is slightly vague about this, but middleware only deals with the HTTP request.
Primus has two ways of extending the functionality. We have plugins but also support middleware. And there is an important difference between these. The middleware layers allows you to modify the incoming requests before they are passed in to the transformers. Plugins allow you to modify and interact with the sparks. The middleware layer is only run for the requests that are handled by Primus.
To achieve what you want, you'll have to create a plugin. It's not much more complicated than middleware.
primus.plugin('herro', {
server: function(primus, options) {
primus.on('connection', function(spark) {
spark.write('Herro Client, I am Server')
})
},
client: function(primus, options) {}
})
For more info, see the Plugins section of the readme.

Node js Reverse Proxy PUT/POST Requests w/ Couchbase Sync Gateway

I'm trying to build a reverse proxy in front of a Couchbase SyncGateway. Before sending requests to the sync gateway, I'd like to send them to an authentication server for authentication, then if all is good, send the request on (unmodified from original) to the sync gateway. The database is not staying up to date with the client modifications and I believe this is because I am not successfully proxying PUT/POST requests. Here is the code I have:
var http = require('http');
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var request = require('request').defaults({json: true});
var authServer = 'http://authserverdns:5000';
var syncGateway = 'http://syncgatewaydns:4984';
http.createServer(function (req, res) {
if (req.method == 'POST' || req.method == 'PUT') {
req.body = '';
req.addListener('data', function(chunk) {
req.body += chunk;
});
req.addListener('end', function() {
processRequest(req, res);
});
} else {
processRequest(req, res);
}
}).listen(8080);
function processRequest(req, res) {
request(authServer, function(error, response, body) {
if (body.authenticated) {
console.log('authenticated !!!');
apiProxy.web(this.req, this.res, {target: this.sg});
} else {
console.log('request denied !!!');
}
}.bind({req: req, res: res, sg: syncGateway}));
}
At first I was using an express server and having same issue. As I looked into the problem, it looks like maybe there is an issue with Express and proxying PUT/POST requests. So, I attempted to use some examples out there and this is what I've ended up with, but still not working. Any ideas as to where I'm going wrong here? Authenticated prints, so I know I'm getting to the point of proxying. And the sync gateway seems to be fine with the GET requests.
Thanks
ugh. I wasn't adding the rest of the URL to the forwarding address for the Sync Gateway. The post here helped.

Using node.js to proxy http and websocket traffic

I'm expanding on a node.js reverse proxy I put together with mikeal's request module: https://github.com/mikeal/request . It is a connect app that uses a small piece of custom middleware to check if the the current request is for an API route and if so it proxies it to and from a different API server like this:
.use(function(req, res, next){
if( /^\/api\//.test(req.url) ){
req.pipe(request(apiUrl + req.url))
.on('error', function(err){
handlePipeError(err, 'Error piping request to proxy server. Details: ');
})
.pipe(res)
.on('error', function(err){
handlePipeError(err, 'Error piping proxy response to client. Details: ')
});
function handlePipeError(err, customMsg) {
console.log( customMsg, err );
res.writeHead(503, {
'Content-Length': Buffer.byteLength(err.message, 'utf8'),
'Content-Type': 'text/plain' });
res.end(err.message);
}
}
else {
next();
}
There is also some other middleware to handle static file serving and some other stuff. Once all the middleware is setup I start the server like this:
var server = require('http').createServer(app);
server.listen(port)
I would now like to expand this script to allow reverse proxying websocket connections. I tried using the nodejitsu/node-http-proxy to do modify the previous portion of the code like so:
var options = {
ws: true,
target: apiUrl,
secure: false,
xfwd: true
};
var wsProxy = require('http-proxy').createProxyServer(options);
var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head) {
console.log("--- CAUGHT UPGRADE ----");
wsProxy.ws(req, socket, head);
});
server.listen(port);
I notice the upgrade event fires off and the callback function does run but the websockets request isn't actually working (the client seems to never establish a connection). Is there a better way to achieve this

Resources