Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing Brute Force Using Node and Express JS

I'm building a website using Node and Express JS and would like to throttle invalid login attempts. Both to prevent online cracking and to reduce unnecessary database calls. What are some ways in which I can implement this?

like image 433
Dave Avatar asked Oct 30 '13 18:10

Dave


People also ask

Are Express sessions secure?

If you run with https and your physical computer is secure from outsiders, then your express session cookie is protected from outsiders when stored locally and is protected (by https) when in transport to the server.


3 Answers

Maybe something like this might help you get started.

var failures = {};

function tryToLogin() {
    var f = failures[remoteIp];
    if (f && Date.now() < f.nextTry) {
        // Throttled. Can't try yet.
        return res.error();
    }

    // Otherwise do login
    ...
}

function onLoginFail() {
    var f = failures[remoteIp] = failures[remoteIp] || {count: 0, nextTry: new Date()};
    ++f.count;
    f.nextTry.setTime(Date.now() + 2000 * f.count); // Wait another two seconds for every failed attempt
}

function onLoginSuccess() { delete failures[remoteIp]; }

// Clean up people that have given up
var MINS10 = 600000, MINS30 = 3 * MINS10;
setInterval(function() {
    for (var ip in failures) {
        if (Date.now() - failures[ip].nextTry > MINS10) {
            delete failures[ip];
        }
    }
}, MINS30);
like image 99
Trevor Dixon Avatar answered Oct 21 '22 16:10

Trevor Dixon


So after doing some searching, I wasn't able to find a solution I liked so I wrote my own based on Trevor's solution and express-brute. You can find it here.

like image 12
Dave Avatar answered Oct 21 '22 16:10

Dave


rate-limiter-flexible package with Redis or Mongo for distributed apps and in-Memory or with Cluster helps

Here is example with Redis

const { RateLimiterRedis } = require('rate-limiter-flexible');
const Redis = require('ioredis');

const redisClient = new Redis({
  options: {
    enableOfflineQueue: false
  }
});

const opts = {
  redis: redisClient,
  points: 5, // 5 points
  duration: 15 * 60, // Per 15 minutes
  blockDuration: 15 * 60, // block for 15 minutes if more than points consumed 
};

const rateLimiter = new RateLimiterRedis(opts);

app.post('/auth', (req, res, next) => {
  // Consume 1 point for each login attempt
  rateLimiter.consume(req.connection.remoteAddress)
    .then((data) => {
      const loggedIn = loginUser();
      if (!loggedIn) {
        // Message to user
        res.status(400).send(data.remainingPoints + ' attempts left');
      } else {
        // successful login
      }
    })
    .catch((rejRes) => {
      // Blocked
      const secBeforeNext = Math.ceil(rejRes.msBeforeNext / 1000) || 1;
      res.set('Retry-After', String(secBeforeNext));
      res.status(429).send('Too Many Requests');
    });
});
like image 12
Animir Avatar answered Oct 21 '22 15:10

Animir