Nodebestpractices: Limit login attempts

Created on 16 May 2018  路  13Comments  路  Source: goldbergyoni/nodebestpractices

@lirantal @js-kyle @BrunoScheufler Should we add this (login attempts limit) to our security list: it's a very common brute-force technique and addressing that requires understanding of Node tools (probably a middleware that uses some redis-like cache with sliding expiration)?

new best practice security

Most helpful comment

Closing, this bullet was included in the security release

All 13 comments

@i0natan I think that's actually a good idea. I'd go for some sort of cooldown after a set number of failed login attempts, but I'm not sure whether to apply that to an ip address or somewhat more specific which would be better IMO.

More specific - Username. IP restirction is already covered on the LB rate limiting.

This package seems great:
https://www.npmjs.com/package/express-brute

That sounds about right, interesting package!

In my book I cover this one: https://www.npmjs.com/package/limiter

@lirantal I think it worth a bullet, what do you think?

The express-brute supports redis, couldn't see support for out-of-process cache on the later

Sounds good yes. limiter also supports redis AFAIR

Do you mean something like that?

It is really , a major practise. You can check rate-limit-redis for more details. Thanks

var RateLimit = require('express-rate-limit');
var RedisStore = require('rate-limit-redis');

//Prevent Brute Forcing with Rate Limiting
try{
    var limiter = new RateLimit({
        store: new RedisStore({
            expiry: 60,
            prefix: 'app_main_server',
            client: require('redis').createClient()
        }),
        max: 100, // limit each IP to 100 requests per windowMs
        delayMs: 0 // disable delaying - full speed until the max limit is reached
    });
} catch (e) {
    console.error(e);
}


module.exports = {
    limiter
}

Or using express-brute:

var ExpressBrute = require('express-brute'),
    RedisStore = require('express-brute-redis');

var store = new RedisStore({
    host: '127.0.0.1',
    port: 6379
});
var bruteforce = new ExpressBrute(store);

app.post('/auth',
    bruteforce.prevent, // error 403 if we hit this route too often
    function (req, res, next) {
        res.send('Success!');
    }
);

Thanks @i0natan , thats a very nice approach .

On the other hand , if we want a more globalized approach.

with the Maxmind GeoLite2 database and node-maxmind-db for the IP geolocation portion.

var express = require('express');
var RateLimit = require('ratelimit.js').RateLimit;
var ExpressMiddleware = require('ratelimit.js').ExpressMiddleware;
var redis = require('redis');
var app = express();

//Default
var defRateLimiter = new RateLimit(redis.createClient(), [{interval: 1, limit: 10}], 'default_rate_limit');

var defMiddleware = new ExpressMiddleware(defRateLimiter);

//Uk
var ukRateLimiter = new RateLimit(redis.createClient(), [{interval: 1, limit: 20}], 'uk_rate_limit');

var ukMiddleware = new ExpressMiddleware(ukRateLimiter);

var rateLimitResponder = function(req, res, next) {
  res.status(429).json({message: 'Rate limit exceeded.'});
};

var geoLimiters = {
  default: defMiddleware.middleware(rateLimitResponder),
  uk: ukMiddleware.middleware(rateLimitResponder);
};

var limitFn = function(req, res, next) {
  var reqIsoCode = getIsoCode(req);
  var limiterMiddleware = geoLimiters[reqIsoCode] || geoLimiter.default;
  limiterMiddleware(req, res, next);
}; 

app.get('/', limitFn, function(req, res, next) {
  // request is not rate limited
  ...
});

The key is that you must differentiate the two rate limiters by using a custom prefix (see the RateLimit constructors). The way the IP addresses (or other request identifier you specify) won鈥檛 conflict.

@LeoLoupos That looks interesting, can you elaborate why create two limiters or why create one per region? what is "ratelimit.js"?

Ratelimit.js : https://github.com/dudleycarr/ratelimit.js/blob/master/README.md

By the time you have an international server, it is good to apply rate limiting practices for specific regions. Because ,if you can have more powerful machines , on for e.x UK rather than Slovenia, it is the only way to configure different limits.

@LeoLoupos The context is a prevention of brute force: limiting the number of login attempts. Do you see a reason to allow more login attempts from some geo area? even regarding general rate limiting, why the fact that your UK server is stronger than the US server (usually HW is similar and scale is expressed in amount of instances so each instance can carry the same load) matters to how many requests/sec you allow *per user?

Closing, this bullet was included in the security release

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MIKOLAJW197 picture MIKOLAJW197  路  4Comments

rhinonan picture rhinonan  路  5Comments

jfgabriel picture jfgabriel  路  4Comments

bikingbadger picture bikingbadger  路  6Comments

goldbergyoni picture goldbergyoni  路  3Comments