Arkcann
Arkcann

Reputation: 668

Intercept (and potentially deny) web socket upgrade request

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.

Upvotes: 9

Views: 5986

Answers (2)

Ali80
Ali80

Reputation: 8676

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

Upvotes: 6

user1195883
user1195883

Reputation: 824

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;
  }
  //...
});

Upvotes: 4

Related Questions