May 30, 2016

How to add login anomaly detection to Express.js

If you run a web app or site on Node.js chances are you probably use the super popular Express framework. Even if you don't, this post is useful if you want to make your Node app more secure by adding login anomaly detection.

The high level overview

In a nutshell we're going to track logins and other authentication related events, by sending them to ThisData's anomaly detection API. If ThisData detects something unusual it can automatically send out a "Was this you?" notification to your user via email or text message. If the user indicates it was not them, ThisData will send a webhook to your app. We'll use the webhook to trigger an automated response to the threat, by killing the user session.

You'll need a ThisData account if you want to follow along. It's free and you can sign up here.

Step 1: Setup ThisData

As you might expect, ThisData has a convenient Node.js SDK which you can install from NPM.

> npm install thisdata --save

Now you want to require ThisData into your Express app and specify your API key.

var ThisData = require('thisdata');  
var thisdata = ThisData('api-key-goes-here');  

If you're not sure where to find that API key, take a look at the API Settings area of your ThisData account.

Step 2: Track an event

You can think of ThisData as a supercharged audit log for your app, where you can literally track any event or user activity. In this case we will focus on log-in but we could also use this to track other account related events like password reset, 2FA usage, or failed logins. In fact, the more events you track the better ThisData will get at detecting the bad actors.

Another reason that we will focus on log-in is that ThisData treats it in a special way; it has the ability to trigger email notifications to your users. Of course you don't have to enable the email notifications and can just work with our webhooks instead, but nevertheless, log-in is special.

The track method on the ThisData client accepts the request object and an options hash.

thisdata.track(req, options);  

Using the default Express project scaffold as a starting point I have extended routes/index.js to demonstrate how you might track events around log-in. In a production scenario you'd probably structure your project a bit differently and use a lib like Passport for authentication, but this should at least point you down the right path.

var express = require('express');  
var router = express.Router();  
var bodyParser = require('body-parser');  
var ThisData = require('thisdata');  
var thisdata = ThisData('fake-api-key');

app.use(bodyParser.urlencoded({ extended: false }));  
app.use(bodyParser.json());

router.get('/', function(req, res, next) {  
  res.render('index', { title: 'Express' });
});

router.post('/login', function(req, res, next) {  
  userStore.authenticate(req.body.user, req.body.pass, function(user){
    if(user){
      // Track successful login to ThisData
      thisdata.track(req, {
        verb: thisdata.verbs.LOG_IN,
        user: {
          id: user.id,
          name: user.fullName,
          email: user.email
        }
      });
      return res.redirect('/home');
    } else {
      // Track failed login to ThisData
      thisdata.track(req, {
        verb: thisdata.verbs.LOG_IN_DENIED
      });
      return res.redirect('/login');
    }
  });
});

router.get('/home', function(req, res, next) {  
  res.render('home', { title: 'Home' });
});

module.exports = router;  

By passing in req the track method will automatically extract the user's IP address and User Agent. You can override these values if needed, and specify additional information to identify the user in the options hash.

A complete options hash would look like this

{
  verb: "password-reset",
  ip: "0.0.0.0",
  userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
  user: {
    id: "12345",
    name: "John Titor",
    email: "[email protected]",
    mobile: "+15555555555"
  }
}

For more detail on what can be tracked in ThisData, read our documentation or take a look at the github repo.

You can also get a complete list of commonly used verbs, and you're able to use your own too.

Step 3: Respond to a webhook

Automating your security workflows is a great idea. It helps reduce the support and investigation workload, and dramatically increases your response time to confirmed threats.

When enabled, ThisData will push webhooks to your app when anomalies are detected or when a user responds to a notification email. The webhook payload body will contain details of the anomaly, including the ID of the affected account. It will also contain a signature that can be used to validate that the webhook truly came from ThisData.

You can setup your webhook endpoint and a shared secret key (that we will use to create the signature) in the API settings area of the ThisData UI.

When you receive a webhook from us, you should first verify the signature. To do this pass the signature from the X-Signature header, your shared secret, and the webhook payload to the validateWebhook method of the ThisData client.

router.post('/webhook', function(req, res, next) {  
  var signature = req.headers['X-Signature'];
  var body = req.body;
  // Don't store your shared secret in source code IRL :-)
  var secret = "hello-world";

  if(thisdata.validateWebhook(secret, signature, body)){
    // This is a real webhook from ThisData

    if(!body.was_user){
      // Uh oh!! The user has confirmed their account was compromised.
      // Kill the user session, and reset their password here.
    }
  }
});

How you use the webhooks is really up to you, but we recommend responding with force if a user indicates that it was not them. If possible you should end all active sessions, and reset their password.

To find out more about webhooks and expected payloads see the webhook documentation.

Conclusion

Hopefully this post has given you an overview of ThisData, and shown you how easy it is to add advanced anomaly detection to your app. You've also learned how to distribute your alert investigation workload and respond quickly when a user confirms suspicious activity.

If you'd like to discuss any of the concepts or workflows I mentioned we'd be happy to help so get in touch.

Thanks for reading :-)

YOU MAY ALSO BE INTERESTED IN

The future of authentication

Today I’m excited to announce a deal that we have been working on for the past few months and how that will impact the future of contextual ...

Introducing custom security rules

For the past few years we’ve been working hard to create a plug and play adaptive risk engine. We designed our core service using a mix of b ...