我正在使用Node和Express JS构建网站,并希望限制无效的登录尝试。两者都可以防止在线破解并减少不必要的数据库调用。我有哪些方法可以实现这个目标?
答案 0 :(得分:7)
所以在做了一些搜索之后,我无法找到我喜欢的解决方案,所以我根据Trevor的解决方案和快速创建了自己的解决方案。你可以找到它here。
答案 1 :(得分:5)
也许这样的事情可能会帮助你开始。
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);
答案 2 :(得分:3)
rate-limiter-flexible包用于分布式应用程序和内存或群集帮助
以下是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
}
});
答案 3 :(得分:1)
好的,我在mongoose和expressjs找到了最大登录尝试的错误密码的解决方案。这是一个解决方案。 *首先我们将定义用户架构 *第二,我们将在wrongpassword处理函数上定义最大登录次数。 *第三,当我们创建登录api然后我们将检查此功能用户登录错误密码的次数。为代码做好准备
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);
};
这是我的登录功能,我们检查了错误密码的最大登录尝试次数。我们将调用此函数
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.' });
});
}
});
});
}));
答案 4 :(得分:1)
看看这个: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.
答案 5 :(得分:0)
我本人想知道如何解决此问题,但是我尝试了以下方法,但不确定性能和良好代码方面的优势。
基本上,我在架构中创建了一个称为“登录尝试”的标志并将其设置为0
然后在登录过程中,执行以下操作:比较密码(如果可以),然后登录。否则,每当用户输入错误密码时,我都会在数据库中增加登录尝试标志。如果登录尝试次数超过3,我会显示一条错误消息,表明您超出了登录尝试次数。
到目前为止,一切正常,下一部分是将标志切换为零的方法。
现在,我使用setTimeout函数在5分钟后运行,并将该标志切换为0,然后它开始工作。
我主要关心的是:这样使用setTimeout安全吗?
另一个问题是这将如何影响性能。
因此,就完成工作而言,它是可行的,但是就性能和最佳方法而言,我对此并不肯定。