0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

Added spam prevention for v2 sessions (#10030)

no-issue

- Added spam prevention to POST /session
  - This blocks repeated requests the the /session endpoint preventing brute
force password attacks
- Updated session controller to reset brute middleware
  - This updates the session controller to reset the brute force protection
on a successful login. This is required so that a user is not locked out
forever :o!!
This commit is contained in:
Fabien O'Carroll 2018-10-18 15:58:29 +07:00 committed by Kevin Ansfield
parent fd958addb6
commit ae71f2deca
3 changed files with 47 additions and 5 deletions

View file

@ -25,8 +25,13 @@ const session = {
password: object.password
}).then((user) => {
return Promise.resolve((req, res, next) => {
req.user = user;
auth.session.createSession(req, res, next);
req.brute.reset(function (err) {
if (err) {
return next(err);
}
req.user = user;
auth.session.createSession(req, res, next);
});
});
}).catch((err) => {
throw new common.errors.UnauthorizedError({

View file

@ -148,7 +148,11 @@ module.exports = function apiRoutes() {
// ## Sessions
router.get('/session', mw.authAdminAPI, api.http(apiv2.session.read));
// We don't need auth when creating a new session (logging in)
router.post('/session', api.http(apiv2.session.add));
router.post('/session',
shared.middlewares.brute.globalBlock,
shared.middlewares.brute.userLogin,
api.http(apiv2.session.add)
);
router.del('/session', mw.authAdminAPI, api.http(apiv2.session.delete));
// ## Authentication

View file

@ -49,8 +49,12 @@ describe('Session controller', function () {
});
});
it('it returns a function that sets req.user and calls createSession if the check works', function () {
const fakeReq = {};
it('it returns a function that calls req.brute.reset, sets req.user and calls createSession if the check works', function () {
const fakeReq = {
brute: {
reset: sandbox.stub().callsArg(0)
}
};
const fakeRes = {};
const fakeNext = () => {};
const fakeUser = models.User.forge({});
@ -65,6 +69,8 @@ describe('Session controller', function () {
}, {}).then((fn) => {
fn(fakeReq, fakeRes, fakeNext);
}).then(function () {
should.equal(fakeReq.brute.reset.callCount, 1);
const createSessionStubCall = createSessionStub.getCall(0);
should.equal(fakeReq.user, fakeUser);
should.equal(createSessionStubCall.args[0], fakeReq);
@ -72,6 +78,33 @@ describe('Session controller', function () {
should.equal(createSessionStubCall.args[2], fakeNext);
});
});
it('it returns a function that calls req.brute.reset and calls next if reset errors', function () {
const resetError = new Error();
const fakeReq = {
brute: {
reset: sandbox.stub().callsArgWith(0, resetError)
}
};
const fakeRes = {};
const fakeNext = sandbox.stub();
const fakeUser = models.User.forge({});
sandbox.stub(models.User, 'check')
.resolves(fakeUser);
const createSessionStub = sandbox.stub(sessionServiceMiddleware, 'createSession');
return sessionController.add({
username: 'freddy@vodafone.com',
password: 'qu33nRul35'
}, {}).then((fn) => {
fn(fakeReq, fakeRes, fakeNext);
}).then(function () {
should.equal(fakeReq.brute.reset.callCount, 1);
should.equal(fakeNext.callCount, 1);
should.equal(fakeNext.args[0][0], resetError);
});
});
});
describe('#delete', function () {