Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Intercept WebSocket messages

With ajax requests it can be done with this code:

let oldXHROpen = window.XMLHttpRequest.prototype.open;
window.lastXhr = '';
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
  this.addEventListener('load', function() {
    window.lastXhr = this.responseText;
  });
  return oldXHROpen.apply(this, arguments);
};

lastXhr variable will hold the last response.

But how can this be achieved for websockets too?

like image 204
Alex Avatar asked Dec 01 '22 08:12

Alex


2 Answers

you would need to make this wrapper as soon as possible

For that answer @brunoff you're correct.. and you can always use your functions before server's by puppet window logic, OR you can hijack the data from MessageEvent itself

function listen(fn){
  fn=fn||console.log
  let property=Object.getOwnPropertyDescriptor
  (MessageEvent.prototype,"data")
  const data=property.get
  function lookAtMessage(){ //to replace get function
    let socket=this.currentTarget instanceof WebSocket
    if(!socket){return data.call(this)}
    let msg=data.call(this)
    Object.defineProperty(this,"data",{value:msg}) //anti-loop
    fn({data:msg,socket:this.currentTarget,event:this})
    return msg
  }
  property.get=lookAtMessage
  Object.defineProperty
  (MessageEvent.prototype,"data",property)
}
listen( ({data})=>console.log(data) )

You can try putting in the code and running it in the console on this page and then running their websocket example

like image 186
The Bomb Squad Avatar answered Dec 04 '22 01:12

The Bomb Squad


To intercept the messages, you will have to spy on the onmessage = fn and addEventListener("message", fn) calls.

To be able to modify the onmessage we have to override the global WebSocket in the first place. The below is intercepting the incoming messages, but in a similar way you can spy on the send method to intercept the outgoing messages (the ones sent by the client to the server).

I tested this on a page using Firebase and it works nicely, but you have to initialize it before the other scripts making sure that the websocket library (it can be socket.io, ws, etc) is using the overridden WebSocket constructor.

Spy the Incoming Messages and modify the data

Eventually you can override the data before calling the real message listener – this becomes handy if you do not have control over the page functionality and want to inject your own data in the message listener.

const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
  console.log("Intercepting web socket creation")

  const ws = new OriginalWebsocket(...arguments)

  const originalAddEventListener = ws.addEventListener
  const proxiedAddEventListener = function() {
    if (arguments[0] === "message") {
      const cb = arguments[1]
      arguments[1] = function() {
        // Here you can get the actual data from the incoming messages
        // Here you can even change the data before calling the real message listener
        Object.defineProperty(e, "data", { value: 'your injected data' })
        console.log("intercepted", arguments[0].data)
        return cb.apply(this, arguments)
      }
    }
    return originalAddEventListener.apply(this, arguments)
  }
  ws.addEventListener = proxiedAddEventListener

  Object.defineProperty(ws, "onmessage", {
    set(func) {
      return proxiedAddEventListener.apply(this, [
        "message",
        func,
        false
      ]);
    }
  });
  return ws;
};

window.WebSocket = ProxiedWebSocket;

If you do not need to modify the data, you can follow the second part of the answer.

Spy the Incoming messages without modifying the data

If you want to listen for messages only, without overriding the data, things are simpler:

const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
  const ws = new OriginalWebsocket(...arguments)
  ws.addEventListener("message", function (e) {
    // Only intercept
    console.log(e.data)
  })
  return ws;
};
window.WebSocket = ProxiedWebSocket;

Spy the Outgoing Messages

In a very similar way, you can proxy the send method which is used to send data to the server.

const OriginalWebsocket = window.WebSocket
const ProxiedWebSocket = function() {
  const ws = new OriginalWebsocket(...arguments)
  const originalSend = ws.send
  const proxiedSend = function() {
    console.log("Intercepted outgoing ws message", arguments)
    // Eventually change the sent data
    // arguments[0] = ...
    // arguments[1] = ...
    return originalSend.apply(this, arguments)
  }
  ws.send = proxiedSend
  return ws;
};
window.WebSocket = ProxiedWebSocket;

Feel free to ask any questions if anything is unclear.

like image 25
Ionică Bizău Avatar answered Dec 04 '22 00:12

Ionică Bizău