Certificate Pinning for WebSockets

I have seen many implementations of certificate pinning (and public key pinning) for HTTPS connections originated from client-side apps running on web browsers and mobile devices.

I would like to know whether such certificate pinning implementations are available for websockets. In the client side (say a mobile device or web browser), can we actually implement certificate pinning / public key pinning for websockets?

If such approach is available, it would be really nice to have an explanation, ideally with links to resources/ articles/ code snippets/ libraries.

capturing web-sockets packets from IOS using fiddler

I am using latest Fiddler version trying to capture web-sockets requests from an IOS application.

I am able to see only HTTP and HTTPS requests.

Laptop(Running fiddler on port 8888) and mobile both are on same network

  • Laptop IP – 192.168.4.2
  • mobile IP – 192.168.4.3 proxy configured to 192.168.4.2 port 8888

Is there any thing am missing out?..

Bypassing valid certificate requirement for websockets

I’m attempting to connect to a websocket server hosted in C# using SuperWebSockets from a page. The issue is that the websocket server generates a self-signed certificate making it difficult to connect to from the browser. The ServerConfig of the SuperWebSocket has “tls” for Security and passes on a self-signed certificate filepath through the Certificate member of the ServerConfig.

I was wondering if anyone knew any hacky tricks to connect to this web socket server from a web page, I’m open to ideas for either/both Firefox or Chrome. I know there are ways to do this if the user adds an exception for the certificate or disables certificate validation, but I need a method that’s pure javascript. Perhaps emulating TLS?

Is there a WebSockets server that accepts connections and can be controlled via HTTP?

I have a little RabbitMQ cluster and I want clients to be able to see results from jobs in that cluster. I don’t want them to connect to RabbitMQ directly for security reasons. Is there a simple client-facing server I can use to accept WebSocket connections from the clients through which I can send my clients messages? I’m hoping for something with some API over HTTP that I can just connect to from my backend code and send a message over a specific topic. If any clients happen to be connected at that point, they should get the update.

Basically a simple messaging server that works over WebSockets so I don’t have to reconfigure my backend application. I’m pretty sure RabbitMQ itself can be used for this but I’m guessing that’s not the best idea since it’s pretty complicated and not intended for direct client connections. If nothing like this is possible, I will likely have to write one myself but I was hoping for something a bit more battle-tested.

Web turn based game Architecture. With or without websockets?

I have written a turn based game (Dominoes) using Scala and Akka actors, where a Server manage all the game state and the clients (Players) receive the new state every time one of them plays a card.

I would like to implement a web version for this but I am having hard time figuring out if this kind of schema needs or not using websockets to consume the messages being sent from the Server to all the players. There is no a heavy interaction between server and clients, but it is not clear for me how players will know that its their turn.

I have been reading some material (Reactive Web Applications, Akka and Play documentation and so on), on how to communicate actors. The Game server is a standalone app. Right now, the client app, when starts, connects to the Server using the preStart method:

...    context.system.actorSelection("akka.tcp://GameServer@127.0.0.1:47001/user/gamelobby").resolveOne()(10.seconds).onComplete(x => x match {             case Success(ref: ActorRef) => {               //sends server message with the name of the player             }             case Failure(t) => {               //fail case             }           })  

On the material I have been reading, it is not clear for me how a client app would connect to the server using websockets. My two main concerns are: It is necessary to use websockets in this scenario? and if its so, how the server and the client will connect?

how to setup secure websockets with nginx

I have a webserver running on port 9000 I want to make it available on port 80, and also I want to make a websocket connection available on port 9021. If i run this over http everything works fine. But when I go to https the websocket cannot be connected.

Here’s my nginx config: this gives the warning:

nginx: [warn] conflicting server name "oyun.net" on 0.0.0.0:443, ignored

server {      listen 443 ssl;      server_name          oyun.net;      ssl_certificate      /etc/key.pem      ssl_certificate_key  /etc/key2.pem      listen 80;      location / {          proxy_pass http://localhost:9000      } }  server {      listen 443 ssl;      server_name          oyun.net;      ssl_certificate      /etc/key.pem      ssl_certificate_key  /etc/key2.pem      listen 9021;      location / {         proxy_pass http://localhost:9000;         proxy_http_version 1.1;         proxy_set_header upgrade $  http_upgrade;         proxy_set_header connection "upgrade";         proxy_set_header x-real-ip $  remote_addr;         proxy_set_header host $  host;         proxy_set_header x-forwarded-for $  proxy_add_x_forwarded_for;      }  } 

Here’s the browser error:

WebSocket connection to 'wss://oyun.net:9021/socket/v1?sri=tcylqwzjnl' failed:   Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR 

Node websockets

I have this code I use it for two things

  1. connect to lxd instances and emits any operation events it recieves over socket.io to the client
  2. Bridge between client and lxd for terminals

Its pretty poorly written so any pointers would be great (but it works fine)

// This originated from https://gist.github.com/CalebEverett/bed94582b437ffe88f650819d772b682 // and was modified to suite our needs const fs = require('fs'),     WebSocket = require('ws'),     express = require('express'),     https = require('https'),     mysql = require('mysql'),     expressWs = require('express-ws'),     path = require('path'),     cors = require('cors');  const envImportResult = require('dotenv').config({     path: "/var/www/LxdMosaic/.env" });  if (envImportResult.error) {     throw envImportResult.error }  // Https certificate and key file location for secure websockets + https server var privateKey = fs.readFileSync(process.env.CERT_PRIVATE_KEY, 'utf8'),     certificate = fs.readFileSync(process.env.CERT_PATH, 'utf8');     certDir = "/var/www/LxdMosaic/src/sensitiveData/certs/",     lxdConsoles = [],     credentials = {         key: privateKey,         cert: certificate     },     app = express();  app.use(cors()); var bodyParser = require('body-parser') app.use( bodyParser.json() );       // to support JSON-encoded bodies app.use(bodyParser.urlencoded({     // to support URL-encoded bodies   extended: true }));  var httpsServer = https.createServer(credentials, app); var io = require('socket.io')(httpsServer);  var operationSocket = io.of("/operations") // expressWs(app, httpsServer);  var con = mysql.createConnection({     host: process.env.DB_HOST,     user: process.env.DB_USER,     password: process.env.DB_PASS,     database: process.env.DB_NAME });  var hostDetails = {};  function createExecOptions(host, container) {     return {         method: 'POST',         host: hostDetails[host].hostWithOutProtoOrPort,         port: hostDetails[host].port,         path: '/1.0/containers/' + container + '/exec',         cert: fs.readFileSync(hostDetails[host].cert),         key: fs.readFileSync(hostDetails[host].key),         rejectUnauthorized: false     } }  const lxdExecBody = JSON.stringify({     "command": ["bash"],     "environment": {         "HOME": "/root",         "TERM": "xterm",         "USER": "root"     },     "wait-for-websocket": true,     "interactive": true, })   con.connect(function(err) {     if (err) {         throw err;     } });  function createWebSockets() {     con.query("SELECT * FROM Hosts", function(err, result, fields) {         if (err) {             throw err;         }         for (i = 0; i < result.length; i++) {             let lxdClientCert = certDir + result[i].Host_Cert_Only_File             let lxdClientKey = certDir + result[i].Host_Key_File              if(result[i].Host_Online == 0){                 continue;             }              // Connecting to the lxd server/s             const wsoptions = {                 cert: fs.readFileSync(lxdClientCert),                 key: fs.readFileSync(lxdClientKey),                 rejectUnauthorized: false,             }              var portRegex = /:[0-9]+/;              let stringUrl = result[i].Host_Url_And_Port;             let urlURL = new URL(result[i].Host_Url_And_Port);              let hostWithOutProto = stringUrl.replace("https://", "");             let hostWithOutProtoOrPort = hostWithOutProto.replace(portRegex, "");              hostDetails[result[i].Host_Url_And_Port] = {                 cert: lxdClientCert,                 key: lxdClientKey,                 hostWithOutProtoOrPort: hostWithOutProtoOrPort,                 port: urlURL.port             };              var ws = new WebSocket('wss://' + hostWithOutProto + '/1.0/events?type=operation', wsoptions);              ws.on('message', function(data, flags) {                 var buf = Buffer.from(data)                 let message = JSON.parse(data.toString());                 message.host = hostWithOutProtoOrPort;                 operationSocket.emit('operationUpdate', message);             });         }     }); }   httpsServer.listen(3000, function() {});   app.get('/hosts/reload/', function(req, res) {     createWebSockets();     res.send({         success: "reloaded"     }); });  app.post('/hosts/message/', function(req, res) {     console.log(req.body.data);     operationSocket.emit(req.body.type, req.body.data);     res.send({         success: "delivered"     }); });  app.get('/', function(req, res) {     res.sendFile(path.join(__dirname + '/index.html')); });  var terminalsIo = io.of("/terminals");  terminalsIo.on("connect", function(socket) {      let indentifier = socket.handshake.query.pid;      if(lxdConsoles[indentifier] == undefined) {         let host = socket.handshake.query.host;         let container = socket.handshake.query.container;           let execOptions = createExecOptions(host, container);          const wsoptions = {             cert: execOptions.cert,             key: execOptions.key,             rejectUnauthorized: false         }          const lxdReq = https.request(execOptions, res => {             res.on('data', d => {                  const output = JSON.parse(d);                  if(output.hasOwnProperty("error") && output.error !== ""){                     socket.emit("data", "Container Offline");                     return false;                 }                  const lxdWs = new WebSocket('wss://' +                     execOptions.host + ':' + execOptions.port + output.operation +                     '/websocket?secret=' + output.metadata.metadata.fds['0'],                     wsoptions                 );                  lxdWs.on('error', error => console.log(error));                  lxdWs.on('message', data => {                     try {                         const buf = Buffer.from(data);                         data = buf.toString();                         socket.emit("data", data);                     } catch (ex) {                         // The WebSocket is not open, ignore                     }                 });                 lxdConsoles.push(lxdWs);             });         });         lxdReq.write(lxdExecBody);         lxdReq.end();     }      //NOTE When user inputs from browser     socket.on('data', function(msg) {         lxdConsoles[indentifier].send(msg, {             binary: true         }, () => {});     });      socket.on('close', function(indentifier) {         setTimeout(() => {             if(lxdConsoles[indentifier] == undefined){                 return             }              lxdConsoles[indentifier].send('exit  \r', { binary: true }, function(){                 lxdConsoles[indentifier].close();                 delete lxdConsoles[indentifier];             });          }, 100);     }); });  app.post('/terminals', function(req, res) {     // Create a indentifier for the console, this should allow multiple consolses     // per user     res.send(lxdConsoles.length.toString()); });   createWebSockets();  ```  

Understanding a DDP client using WebSockets and EventEmitter

I came accross a code about a DDP client I am trying to understand.

The code contains two main classes: DDPSocket which inherits from WebSocketClient and from EventEmitter. The second class inherits too from the EventEmitter class and implements an object of type DDPSocket. I have the following questions:

  1. Why does DDPSocket need to inherit from EventEmitter?

  2. Are the overriden methods from the websockets standard methods required to ensure the communication with the server?

  3. Why does the overriden Websockets methods call emit from EventEmitter? Why don’t they just return a string or other results to the server?
  4. How can the EventEmitter detect that the server sent a notification to the client? Here, I am trying to understand why the call in _init_socket to the following three lines enough.

    self.ddpsocket.on('received_message', self.received_message) self.ddpsocket.on('closed', self.closed) self.ddpsocket.on('opened', self.opened) 

Here is the full code:

import sys import ejson import time import socket  from ws4py.exc import WebSocketException from ws4py.client.threadedclient import WebSocketClient from pyee import EventEmitter  DDP_VERSIONS = ["1", "pre2", "pre1"]  class DDPSocket(WebSocketClient, EventEmitter):     """DDPSocket"""     def __init__(self, url, debug=False):         self.debug = debug         # by default socket connections don't timeout. this causes issues         # where reconnects can get stuck         # TODO: make this configurable?         socket.setdefaulttimeout(10)         WebSocketClient.__init__(self, url)         EventEmitter.__init__(self)      def opened(self):         """Set the connect flag to true and send the connect message to         the server."""         self.emit('opened')      def closed(self, code, reason=None):         """Called when the connection is closed"""         self.emit('closed', code, reason)      def send(self, msg_dict):         """Send a message through the websocket client and wait for the         answer if the message being sent contains an id attribute."""         message = ejson.dumps(msg_dict)         super(DDPSocket, self).send(message)         self._debug_log('<<<{}'.format(message))      def received_message(self, data):         self._debug_log('>>>{}'.format(data))         self.emit('received_message', data)      def _debug_log(self, msg):         """Debug log messages if debug=True"""         if not self.debug:             return         sys.stderr.write('{}\n'.format(msg))      def once(self):         # check for self.sock existence         # https://github.com/hharnisc/python-meteor/issues/5         if self.sock:             return super(DDPSocket, self).once()         return False   class DDPClient(EventEmitter):     """An event driven ddp client"""     def __init__(self, url, auto_reconnect=True, auto_reconnect_timeout=0.5, debug=False):         EventEmitter.__init__(self)         self.ddpsocket = None         self._ddp_version_index = 0         self._retry_new_version = False         self._is_closing = False         self._is_reconnecting = False         self.url = url         self.auto_reconnect = auto_reconnect         self.auto_reconnect_timeout = auto_reconnect_timeout         self.debug = debug         self._session = None         self._uniq_id = 0         self._callbacks = {}         self._init_socket()      def _init_socket(self):         """Initialize the ddp socket"""         # destroy the connection if it already exists         if self.ddpsocket:             self.ddpsocket.remove_all_listeners('received_message')             self.ddpsocket.remove_all_listeners('closed')             self.ddpsocket.remove_all_listeners('opened')             self.ddpsocket.close_connection()             self.ddpsocket = None          # create a ddp socket and subscribe to events         self.ddpsocket = DDPSocket(self.url, self.debug)         self.ddpsocket.on('received_message', self.received_message)         self.ddpsocket.on('closed', self.closed)         self.ddpsocket.on('opened', self.opened)      def _recover_network_failure(self):         """Recover from a network failure"""         if self.auto_reconnect and not self._is_closing:             connected = False             while not connected:                 log_msg = "* ATTEMPTING RECONNECT"                 if self._retry_new_version:                     log_msg = "* RETRYING DIFFERENT DDP VERSION"                 self.ddpsocket._debug_log(log_msg)                 time.sleep(self.auto_reconnect_timeout)                 self._init_socket()                 try:                     self.connect()                     connected = True                     if self._retry_new_version:                         self._retry_new_version = False                     else:                         self._is_reconnecting = True                 except (socket.error, WebSocketException):                     pass      def _next_id(self):         """Get the next id that will be sent to the server"""         self._uniq_id += 1         return str(self._uniq_id)      def connect(self):         if self.ddpsocket:             self.ddpsocket.connect()      def close(self):         self._is_closing = True         self.ddpsocket.close_connection()      def opened(self):         """Send the connect message to the server."""         # give up if there are no more ddp versions to try         if self._ddp_version_index == len(DDP_VERSIONS):             self.ddpsocket._debug_log('* DDP VERSION MISMATCH')             self.emit('version_mismatch', DDP_VERSIONS)             return          # use server recommended version if we support it         if self._retry_new_version in DDP_VERSIONS:             self._ddp_version_index = [i for i, x in enumerate(DDP_VERSIONS)                                        if x == self._retry_new_version][0]          connect_msg = {             "msg": "connect",             "version": DDP_VERSIONS[self._ddp_version_index],             "support": DDP_VERSIONS         }          # if we've already got a session token then reconnect         if self._session:             connect_msg["session"] = self._session          self.send(connect_msg)      def closed(self, code, reason=None):         """Called when the connection is closed"""         self.emit('socket_closed', code, reason)         self._recover_network_failure()      def send(self, msg_dict):         """Send a message through the websocket client and wait for the         answer if the message being sent contains an id attribute."""         self.ddpsocket.send(msg_dict)      def received_message(self, data):         """Incomming messages"""         data = ejson.loads(str(data))         if not data.get('msg'):             return          elif data['msg'] == 'failed':             self._ddp_version_index += 1             self._retry_new_version = data.get('version', True)             self.emit('failed', data)          elif data['msg'] == 'connected':             self._session = data.get('session')             if self._is_reconnecting:                 self.ddpsocket._debug_log("* RECONNECTED")                 self.emit('reconnected')                 self._is_reconnecting = False             else:                 self.ddpsocket._debug_log("* CONNECTED")                 self.emit('connected')                 self._retry_new_version = False          # method result         elif data['msg'] == 'result':             # call the optional callback             callback = self._callbacks.get(data['id'])             if callback:                 callback(data.get('error'), data.get('result'))                 self._callbacks.pop(data['id'])          # missing subscription         elif data['msg'] == 'nosub':             callback = self._callbacks.get(data['id'])             if callback:                 callback(data.get('error'), data['id'])                 self._callbacks.pop(data['id'])          # document added to collection         elif data['msg'] == 'added':             self.emit('added', data['collection'],                       data['id'], data.get('fields', {}))          # document changed in collection         elif data['msg'] == 'changed':             self.emit('changed', data['collection'], data['id'],                        data.get('fields', {}), data.get('cleared', {}))          # document removed from collection         elif data['msg'] == 'removed':             self.emit('removed', data['collection'], data['id'])          # subcription ready         elif data['msg'] == 'ready':             for sub_id in data.get('subs', []):                 callback = self._callbacks.get(sub_id)                 if callback:                     callback(data.get('error'), sub_id)                     self._callbacks.pop(sub_id)          elif data['msg'] == 'ping':             msg = {'msg': 'pong'}             id = data.get('id')             if id is not None:                 msg['id'] = id             self.ddpsocket.send(msg)          else:             pass      def call(self, method, params, callback=None):         """Call a method on the server         Arguments:         method - the remote server method         params - an array of commands to send to the method         Keyword Arguments:         callback - a callback function containing the return data"""         cur_id = self._next_id()         if callback:             self._callbacks[cur_id] = callback         self.send({'msg': 'method', 'id': cur_id, 'method': method, 'params': params})      def subscribe(self, name, params, callback=None):         """Subcribe to add/change/remove events for a collection         Arguments:         name - the name of the publication to subscribe         params - params to subscribe (parsed as ejson)         Keyword Arguments:         callback - a callback function that gets executed when the subscription has completed"""         cur_id = self._next_id()         if callback:             self._callbacks[cur_id] = callback         self.send({'msg': 'sub', 'id': cur_id, 'name': name, 'params': params})         return cur_id      def unsubscribe(self, sub_id):         """Unsubscribe from a collection         Arguments:         sub_id - the id of the subsciption (returned by subcribe)"""         self.send({'msg': 'unsub', 'id': sub_id}) 

In case some reader is interested by the full link to the code .