Preventing Brute Force Using Node and Express JS

前端 未结 6 1017
陌清茗
陌清茗 2021-02-05 18:42

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. Wh

相关标签:
6条回答
  • 2021-02-05 18:50

    I myself wondered how to tackle this, but I tried the following and I am not sure how good is it in terms of performance and good code.

    Basically, I created a flag in my Schema called "login attempts" and set it to 0
    Then in the login process, I do the following: compare the password, if it's okay then I log in. Else, I increment the login attempt flag in my DB each time the user enters the wrong password. If the login attempts exceed 3, I display an error message saying that you exceeded login attempts.

    Now up to this point everything works, the next part is pretty much way of switching that flag to zero.

    Now I used setTimeout function to run after 5 mins and switch that flag to 0 and it worked.

    My main concern: Is it safe to use setTimeout like this.

    the other concern is how is this going to affect the performance.

    So in terms of getting the job done, it's working but in terms of performance and best method, I am not sure about that.

    0 讨论(0)
  • 2021-02-05 18:51

    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) => {
      const loggedIn = loginUser();
      if (!loggedIn) {
          // Consume 1 point for each failed login attempt
          rateLimiter.consume(req.connection.remoteAddress)
            .then((data) => {
              // Message to user
              res.status(400).send(data.remainingPoints + ' attempts left');
            })
            .catch((rejRes) => {
              // Blocked
              const secBeforeNext = Math.ceil(rejRes.msBeforeNext / 1000) || 1;
              res.set('Retry-After', String(secBeforeNext));
              res.status(429).send('Too Many Requests');
            });
      } else {
        // successful login
      }
    });
    
    0 讨论(0)
  • 2021-02-05 18:56

    Have a look on this: https://github.com/AdamPflug/express-brute A brute-force protection middleware for express routes that rate-limits incoming requests, increasing the delay with each request in a fibonacci-like sequence.

    0 讨论(0)
  • 2021-02-05 18:57

    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);
    
    0 讨论(0)
  • 2021-02-05 18:57

    okk,i found the solution of max login attemp on wrong password in mongoose and expressjs.there is a solution. *first we will define the user schema *second we will define the max login on wrongpassword handler function. *third when we will create the login api then we will check this function that how many times user login with wrong password.so be ready for code

    var config = require('../config');
    
    
    var userSchema = new mongoose.Schema({
        email: { type: String, unique: true, required: true },
        password: String,
        verificationToken: { type: String, unique: true, required: true },
        isVerified: { type: Boolean, required: true, default: false },
        passwordResetToken: { type: String, unique: true },
        passwordResetExpires: Date,
        loginAttempts: { type: Number, required: true, default: 0 },
        lockUntil: Number,
        role: String
    });
    
    userSchema.virtual('isLocked').get(function() {
        return !!(this.lockUntil && this.lockUntil > Date.now());
    });
    userSchema.methods.incrementLoginAttempts = function(callback) {
        console.log("lock until",this.lockUntil)
        // if we have a previous lock that has expired, restart at 1
        var lockExpired = !!(this.lockUntil && this.lockUntil < Date.now());
    console.log("lockExpired",lockExpired)
        if (lockExpired) {
            return this.update({
                $set: { loginAttempts: 1 },
                $unset: { lockUntil: 1 }
            }, callback);
        }
    // otherwise we're incrementing
        var updates = { $inc: { loginAttempts: 1 } };
             // lock the account if we've reached max attempts and it's not locked already
        var needToLock = !!(this.loginAttempts + 1 >= config.login.maxAttempts && !this.isLocked);
    console.log("needToLock",needToLock)
    console.log("loginAttempts",this.loginAttempts)
        if (needToLock) {
            updates.$set = { lockUntil: Date.now() + config.login.lockoutHours };
            console.log("config.login.lockoutHours",Date.now() + config.login.lockoutHours)
        }
    //console.log("lockUntil",this.lockUntil)
        return this.update(updates, callback);
    };
    

    here is my login function where we have checked the max login attempt on wrong password.so we will call this function

    User.findOne({ email: email }, function(err, user) {
            console.log("i am aurhebengdfhdbndbcxnvndcvb")
            if (!user) {
                return done(null, false, { msg: 'No user with the email ' + email + ' was found.' });
            }
    
            if (user.isLocked) {
                return user.incrementLoginAttempts(function(err) {
                    if (err) {
                        return done(err);
                    }
    
                    return done(null, false, { msg: 'You have exceeded the maximum number of login attempts.  Your account is locked until ' + moment(user.lockUntil).tz(config.server.timezone).format('LT z') + '.  You may attempt to log in again after that time.' });
                });
            }
    
            if (!user.isVerified) {
                return done(null, false, { msg: 'Your email has not been verified.  Check your inbox for a verification email.<p><a href="/user/verify-resend/' + email + '" class="btn waves-effect white black-text"><i class="material-icons left">email</i>Re-send verification email</a></p>' });
            }
    
            user.comparePassword(password, function(err, isMatch) {
                if (isMatch) {
                    return done(null, user);
                }
                else {
                    user.incrementLoginAttempts(function(err) {
                        if (err) {
                            return done(err);
                        }
    
                        return done(null, false, { msg: 'Invalid password.  Please try again.' });
                    });
                }
            });
        });
    }));
    
    0 讨论(0)
  • 2021-02-05 19:00

    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.

    0 讨论(0)
提交回复
热议问题