Fix brute for token exchanges (#7725)

closes #7722

- fixes issue where token exhanges are logged with an undefined email address causing lockouts
- use more relevant translations for errors
This commit is contained in:
David Wolfe 2016-11-17 13:02:56 +00:00 committed by Katharina Irrgang
parent 7eb316b786
commit e2bbf7d206
5 changed files with 49 additions and 25 deletions

View File

@ -9,7 +9,7 @@ var oauth2orize = require('oauth2orize'),
oauthServer,
oauth;
function exchangeRefreshToken(client, refreshToken, scope, done) {
function exchangeRefreshToken(client, refreshToken, scope, body, authInfo, done) {
models.Refreshtoken.findOne({token: refreshToken})
.then(function then(model) {
if (!model) {
@ -21,6 +21,7 @@ function exchangeRefreshToken(client, refreshToken, scope, done) {
refreshExpires = Date.now() + utils.ONE_WEEK_MS;
if (token.expires > Date.now()) {
spamPrevention.userLogin.reset(null, authInfo.ip + body.refresh_token + 'login');
models.Accesstoken.add({
token: accessToken,
user_id: token.user_id,
@ -70,7 +71,6 @@ function exchangeAuthorizationCode(req, res, next) {
message: i18n.t('errors.middleware.auth.accessDenied')
}));
}
req.query.code = req.body.authorizationCode;
passport.authenticate('ghost', {session: false, failWithError: false}, function authenticate(err, user) {
@ -86,6 +86,8 @@ function exchangeAuthorizationCode(req, res, next) {
}));
}
spamPrevention.userLogin.reset(null, req.authInfo.ip + req.body.authorizationCode + 'login');
authenticationAPI.createTokens({}, {context: {client_id: req.client.id, user: user.id}})
.then(function then(response) {
res.json({

View File

@ -54,7 +54,7 @@ globalBlock = new ExpressBrute(store,
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
{rfa: spamGlobalBlock.freeRetries + 1 || 5, rfp: spamGlobalBlock.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
help: i18n.t('errors.middleware.spamprevention.tooManyAttempts')
}));
},
handleStoreError: handleStoreError
@ -86,10 +86,10 @@ userLogin = new ExpressBrute(store,
attachResetToRequest: true,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
{rfa: spamUserLogin.freeRetries + 1 || 5, rfp: spamUserLogin.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
message: 'Too many sign-in attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
// TODO add more options to i18n
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context'),
help: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
},
handleStoreError: handleStoreError
@ -104,10 +104,10 @@ userReset = new ExpressBrute(store,
attachResetToRequest: true,
failCallback: function (req, res, next, nextValidRequestDate) {
return next(new errors.TooManyRequestsError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
message: 'Too many password reset attempts try again in ' + moment(nextValidRequestDate).fromNow(true),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.error',
{rfa: spamUserReset.freeRetries + 1 || 5, rfp: spamUserReset.lifetime || 60 * 60}),
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
help: i18n.t('errors.middleware.spamprevention.forgottenPasswordEmail.context')
}));
},
handleStoreError: handleStoreError
@ -121,13 +121,13 @@ privateBlog = new ExpressBrute(store,
attachResetToRequest: false,
failCallback: function (req, res, next, nextValidRequestDate) {
logging.error(new errors.GhostError({
message: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.error',
message: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.error',
{rfa: spamPrivateBlog.freeRetries + 1 || 5, rfp: spamPrivateBlog.lifetime || 60 * 60}),
context: i18n.t('errors.middleware.spamprevention.forgottenPasswordIp.context')
context: i18n.t('errors.middleware.spamprevention.tooManySigninAttempts.context')
}));
return next(new errors.GhostError({
message: 'Too many attempts try again in ' + moment(nextValidRequestDate).fromNow(true)
message: 'Too many private sign-in attempts try again in ' + moment(nextValidRequestDate).fromNow(true)
}));
},
handleStoreError: handleStoreError

View File

@ -8,6 +8,7 @@ module.exports = {
req.authInfo = req.authInfo || {};
req.authInfo.ip = req.connection.remoteAddress;
req.body.connection = req.connection.remoteAddress;
next(req.authInfo.ip);
}
}),
@ -24,9 +25,21 @@ module.exports = {
ignoreIP: true,
key: function (req, res, next) {
req.authInfo = req.authInfo || {};
req.authInfo.ip = req.connection.remoteAddress;
req.authInfo.ip = req.connection.remoteAddress || req.ip;
// prevent too many attempts for the same username
next(req.authInfo.ip + req.body.username + 'login');
if (req.body.username) {
return next(req.authInfo.ip + req.body.username + 'login');
}
if (req.body.authorizationCode) {
return next(req.authInfo.ip + req.body.authorizationCode + 'login');
}
if (req.body.refresh_token) {
return next(req.authInfo.ip + req.body.refresh_token + 'login');
}
return next();
}
}),
userReset: spamPrevention.userReset.getMiddleware({

View File

@ -29,7 +29,7 @@ describe('Spam Prevention API', function () {
}).then(function (user) {
author = user;
done();
}).then(testUtils.clearBruteData)
})
.catch(done);
});
@ -67,7 +67,7 @@ describe('Spam Prevention API', function () {
var error = res.body.errors[0];
should.exist(error.errorType);
error.errorType.should.eql('TooManyRequestsError');
error.message.should.eql('Too many attempts try again in 10 minutes');
error.message.should.eql('Too many sign-in attempts try again in 10 minutes');
done();
});

View File

@ -43,8 +43,8 @@ describe('OAuth', function () {
req.client = {
slug: 'test'
};
req.authInfo = {};
req.authInfo.ip = '127.0.0.1';
req.connection = {remoteAddress: '127.0.0.1'};
req.authInfo = {ip: '127.0.0.1'};
req.body.grant_type = 'password';
req.body.username = 'username';
@ -95,6 +95,7 @@ describe('OAuth', function () {
slug: 'test'
};
req.authInfo = {ip: '127.0.0.1'};
req.body.grant_type = 'password';
req.body.username = 'username';
req.body.password = 'password';
@ -116,6 +117,7 @@ describe('OAuth', function () {
slug: 'test'
};
req.authInfo = {ip: '127.0.0.1'};
req.body.grant_type = 'password';
req.body.username = 'username';
req.body.password = 'password';
@ -159,7 +161,8 @@ describe('OAuth', function () {
req.client = {
slug: 'test'
};
req.authInfo = {ip: '127.0.0.1'};
req.connection = {remoteAddress: '127.0.0.1'};
req.body.grant_type = 'refresh_token';
req.body.refresh_token = 'token';
res.setHeader = {};
@ -204,7 +207,8 @@ describe('OAuth', function () {
req.client = {
slug: 'test'
};
req.connection = {remoteAddress: '127.0.0.1'};
req.authInfo = {ip: '127.0.0.1'};
req.body.grant_type = 'refresh_token';
req.body.refresh_token = 'token';
res.setHeader = {};
@ -224,7 +228,8 @@ describe('OAuth', function () {
req.client = {
slug: 'test'
};
req.connection = {remoteAddress: '127.0.0.1'};
req.authInfo = {ip: '127.0.0.1'};
req.body.grant_type = 'refresh_token';
req.body.refresh_token = 'token';
res.setHeader = {};
@ -250,7 +255,8 @@ describe('OAuth', function () {
req.client = {
slug: 'test'
};
req.connection = {remoteAddress: '127.0.0.1'};
req.authInfo = {ip: '127.0.0.1'};
req.body.grant_type = 'refresh_token';
req.body.refresh_token = 'token';
res.setHeader = {};
@ -296,7 +302,8 @@ describe('OAuth', function () {
req.client = {
id: 1
};
req.authInfo = {ip: '127.0.0.1'};
req.connection = {remoteAddress: '127.0.0.1'};
req.body.grant_type = 'authorization_code';
req.body.authorizationCode = '1234';
@ -329,6 +336,8 @@ describe('OAuth', function () {
id: 1
};
req.authInfo = {ip: '127.0.0.1'};
req.connection = {remoteAddress: '127.0.0.1'};
req.body.grant_type = 'authorization_code';
req.body.authorizationCode = '1234';
@ -351,7 +360,7 @@ describe('OAuth', function () {
req.client = {
id: 1
};
req.connection = {remoteAddress: '127.0.0.1'};
req.body.grant_type = 'authorization_code';
oAuth.generateAccessToken(req, res, function (err) {