Server side subscribe wrapper for security

Hey everyone,
i am trying to implement additional security mechanisms in my meteor app. The basic idea is to detect attackers before they are successful. To archive this I want to check method calls as well subscription by users on server side for malicious input, e.g. a user trying to call non-existing methods or subscribing to non existing subscriptions. Since an attacker could bypass any checks on client side it has to be checked on the server side.
To check the method calls i wrapped the applyAsync function of Meteor.server and it works like a charm.
Now I am trying to do the same with subscriptions. I found this sub function within session.protocol_handlers, which I tried to warp also.

Since the protocol_handlers is part of a session I wrap it for every session onConnection:

Meteor.startup(()=> {
    Meteor.onConnection((connection) => {
        let session = Meteor.server.sessions.get(connection.id)
        let prototype = Object.getPrototypeOf(session)
        let protocolHandlersSubOriginal = prototype.protocol_handlers.sub.bind(session)

        const newSub = function (msg, unblock) {
               return protocolHandlersSubOriginal(msg, unblock)
        }
        session.protocol_handlers.sub = newSub
    })
})

As you can see, right now I’m only trying to call the original function.
But whenever I try to run my app it only works for the first connection.
Taking a look at the logs it seems that the first user, who set up a connection, defines the connectionId and the userId for every subscription to come. But I really don’t understand why. Has anyone any ideas or know a better way to archive what I am trying to do?
Any help is much appreciated.

2 Likes

The this context object for the pub/sub and update method handlers contains a user ID. Instead of bloating the connection event, you can wrap each handler with a function that authenticates the user:

Meteor.publish( path, _.partial(authSubscription, path, handler) );
. . . 
function authSubscription( path, handler, ...args ){
     authorizeUser( this.userId );
     console.info(`Starting ${path} for ${this.userId}.`);
     this.onStop = _.partial( function(onStop, callback){
        onStop( () => {
            console.info(`Stopping ${path} for ${this.userId}.`);
            callback && callback();
       });
     }, _.bind(this.onStop, this) );
     handler.apply( this, args );
}