Ghost/core/server/middleware/spam-prevention.js
rfpe 7abcc43907 Harvest server side strings
closes #5617
- Replace all hard-coded server-side strings with i18n translations
2015-12-19 12:12:16 +01:00

167 lines
7.0 KiB
JavaScript

// # SpamPrevention Middleware
// Usage: spamPrevention
// After:
// Before:
// App: Admin|Blog|API
//
// Helpers to handle spam detection on signin, forgot password, and protected pages.
var _ = require('lodash'),
errors = require('../errors'),
config = require('../config'),
i18n = require('../i18n'),
loginSecurity = [],
forgottenSecurity = [],
protectedSecurity = [],
spamPrevention;
spamPrevention = {
/*jslint unparam:true*/
// limit signin requests to ten failed requests per IP per hour
signin: function signin(req, res, next) {
var currentTime = process.hrtime()[0],
remoteAddress = req.connection.remoteAddress,
deniedRateLimit = '',
ipCount = '',
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
rateSigninPeriod = config.rateSigninPeriod || 3600,
rateSigninAttempts = config.rateSigninAttempts || 10;
if (req.body.username && req.body.grant_type === 'password') {
loginSecurity.push({ip: remoteAddress, time: currentTime, email: req.body.username});
} else if (req.body.grant_type === 'refresh_token') {
return next();
} else {
return next(new errors.BadRequestError(i18n.t('errors.middleware.spamprevention.noUsername')));
}
// filter entries that are older than rateSigninPeriod
loginSecurity = _.filter(loginSecurity, function filter(logTime) {
return (logTime.time + rateSigninPeriod > currentTime);
});
// check number of tries per IP address
ipCount = _.chain(loginSecurity).countBy('ip').value();
deniedRateLimit = (ipCount[remoteAddress] > rateSigninAttempts);
if (deniedRateLimit) {
errors.logError(
i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error', {rateSigninAttempts: rateSigninAttempts, rateSigninPeriod: rateSigninPeriod}),
i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
);
message += rateSigninPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
return next(new errors.TooManyRequestsError(message));
}
next();
},
// limit forgotten password requests to five requests per IP per hour for different email addresses
// limit forgotten password requests to five requests per email address
forgotten: function forgotten(req, res, next) {
var currentTime = process.hrtime()[0],
remoteAddress = req.connection.remoteAddress,
rateForgottenPeriod = config.rateForgottenPeriod || 3600,
rateForgottenAttempts = config.rateForgottenAttempts || 5,
email = req.body.passwordreset[0].email,
ipCount = '',
deniedRateLimit = '',
deniedEmailRateLimit = '',
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
index = _.findIndex(forgottenSecurity, function findIndex(logTime) {
return (logTime.ip === remoteAddress && logTime.email === email);
});
if (email) {
if (index !== -1) {
forgottenSecurity[index].count = forgottenSecurity[index].count + 1;
} else {
forgottenSecurity.push({ip: remoteAddress, time: currentTime, email: email, count: 0});
}
} else {
return next(new errors.BadRequestError(i18n.t('errors.middleware.spamprevention.noEmail')));
}
// filter entries that are older than rateForgottenPeriod
forgottenSecurity = _.filter(forgottenSecurity, function filter(logTime) {
return (logTime.time + rateForgottenPeriod > currentTime);
});
// check number of tries with different email addresses per IP
ipCount = _.chain(forgottenSecurity).countBy('ip').value();
deniedRateLimit = (ipCount[remoteAddress] > rateForgottenAttempts);
if (index !== -1) {
deniedEmailRateLimit = (forgottenSecurity[index].count > rateForgottenAttempts);
}
if (deniedEmailRateLimit) {
errors.logError(
i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.error', {rfa: rateForgottenAttempts, rfp: rateForgottenPeriod}),
i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.context')
);
}
if (deniedRateLimit) {
errors.logError(
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateForgottenAttempts, rfp: rateForgottenPeriod}),
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
);
}
if (deniedEmailRateLimit || deniedRateLimit) {
message += rateForgottenPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
return next(new errors.TooManyRequestsError(message));
}
next();
},
protected: function protected(req, res, next) {
var currentTime = process.hrtime()[0],
remoteAddress = req.connection.remoteAddress,
rateProtectedPeriod = config.rateProtectedPeriod || 3600,
rateProtectedAttempts = config.rateProtectedAttempts || 10,
ipCount = '',
message = i18n.t('errors.middleware.spamprevention.tooManyAttempts'),
deniedRateLimit = '',
password = req.body.password;
if (password) {
protectedSecurity.push({ip: remoteAddress, time: currentTime});
} else {
res.error = {
message: i18n.t('errors.middleware.spamprevention.noPassword')
};
return next();
}
// filter entries that are older than rateProtectedPeriod
protectedSecurity = _.filter(protectedSecurity, function filter(logTime) {
return (logTime.time + rateProtectedPeriod > currentTime);
});
ipCount = _.chain(protectedSecurity).countBy('ip').value();
deniedRateLimit = (ipCount[remoteAddress] > rateProtectedAttempts);
if (deniedRateLimit) {
errors.logError(
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error', {rfa: rateProtectedAttempts, rfp: rateProtectedPeriod}),
i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
);
message += rateProtectedPeriod === 3600 ? i18n.t('errors.middleware.spamprevention.waitOneHour') : i18n.t('errors.middleware.spamprevention.tryAgainLater');
res.error = {
message: message
};
}
return next();
},
resetCounter: function resetCounter(email) {
loginSecurity = _.filter(loginSecurity, function filter(logTime) {
return (logTime.email !== email);
});
}
};
module.exports = spamPrevention;