Saturday, 6 May 2017

Is it safe to protect NodeJS routes with Redis Store session variables?

I'm pretty new to MEAN stack applications and session flow itself. I have admin dashboard and certain API routes I want to protect to access only by an admin. Also I'm using socket.io with passport sessions stored in Redis Store and checking if user who emitted socket is authenticated and an admin. I'm granting admin rights in the following way: there is a MongoDB collection of users with certain boolean value "isAdmin".

When user authenticates we go:

db.users.findOne({ userID: profile.id }, function(err, user) {
    if (user) {
       // blah-blah-blah
       returnSession(user.isAdmin);
    }
});

function returnSession(isAdmin) {
    if(isAdmin) {
        profile.isAdmin = true;
        return done(null, profile);
    } else {
        return done(null, profile);
    }
};

Then, if I want to protect any API route I'm going:

function isAdmin (req) {
    if (req.isAuthenticated() && req.user.isAdmin) {
        return true;
    } else {
        return false;
    }
}

router.get('/test', function(req, res){
    if(isAdmin(req)) {
        // This user has access
    } else {
        // User is not an admin
    }
});

Here is how I'm passing session to socket.io:

var sessionMW = session({
    store: new RedisStore({ host: 'localhost', port: 6397, client: redis}),
    secret: 'blah-blah', // my secret key to hash sessions
    name: 'nameOfCookie',
    resave: true,
    saveUninitialized: true
});

var app = express();
// ...
app.use(sessionMW);
// ...
app.use(passport.initialize());
app.use(passport.session());
// ...
//Body parser MW
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// ...
passport.session();
//...
io.use(function(socket, next) {
    // Wrap the express middleware
    sessionMW(socket.request, {}, next);
});

Then, if I want to check if certain socket was emitted by an admin:

function isAuthenticated(socket) {
    if(socket.request.session.hasOwnProperty('passport') && socket.request.session.passport.hasOwnProperty('user')) {
        return true;
    } else {
        return false;
    }
};
function isAdmin(socket) {
    if(socket.request.session.hasOwnProperty('passport') && socket.request.session.passport.hasOwnProperty('user')) {
        if(socket.request.session.passport.user.hasOwnProperty('isAdmin')) {
            return socket.request.session.passport.user.isAdmin;
        } else {
            return false;
        }
    } else {
        return false;
    }
};

And the socket event itself:

socket.on('someProtectedEvent', function(data) {
    if(isAdmin(socket)) {
        // User has rights to use this event
    } else {
        // User is not an admin
    }
});

So, are my dashboard, routes and sensitive socket.io events protected enough? I mean, can anyone bypass these verifications in easy way, or just set this boolean "isAdmin" to true in their own session? Or am I going right way and my dashboard and admin-events are enough protected?

Thanks for your help in advance



via Danny

No comments:

Post a Comment