mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Added brute force protection to login
Closes half of #468 * adds a 2 second limit until you can retry logging in, otherwise sends you a 401. * bounce: 2ms, checks the pw: 254ms on my machine * added a test to the casper suite
This commit is contained in:
parent
2b94364842
commit
368eb7a352
3 changed files with 65 additions and 9 deletions
|
@ -13,7 +13,8 @@ var Ghost = require('../../ghost'),
|
|||
ghost = new Ghost(),
|
||||
dataProvider = ghost.dataProvider,
|
||||
adminNavbar,
|
||||
adminControllers;
|
||||
adminControllers,
|
||||
loginSecurity = [];
|
||||
|
||||
// TODO: combine path/navClass to single "slug(?)" variable with no prefix
|
||||
adminNavbar = {
|
||||
|
@ -43,6 +44,7 @@ adminNavbar = {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
// TODO: make this a util or helper
|
||||
function setSelected(list, name) {
|
||||
_.each(list, function (item, key) {
|
||||
|
@ -93,6 +95,17 @@ adminControllers = {
|
|||
});
|
||||
},
|
||||
'auth': function (req, res) {
|
||||
var currentTime = process.hrtime()[0],
|
||||
denied = '';
|
||||
loginSecurity = _.filter(loginSecurity, function (ipTime) {
|
||||
return (ipTime.time + 2 > currentTime);
|
||||
});
|
||||
denied = _.find(loginSecurity, function (ipTime) {
|
||||
return (ipTime.ip === req.connection.remoteAddress);
|
||||
});
|
||||
|
||||
if (!denied) {
|
||||
loginSecurity.push({ip: req.connection.remoteAddress, time: process.hrtime()[0]});
|
||||
api.users.check({email: req.body.email, pw: req.body.password}).then(function (user) {
|
||||
req.session.user = user.id;
|
||||
res.json(200, {redirect: req.body.redirect ? '/ghost/'
|
||||
|
@ -100,6 +113,10 @@ adminControllers = {
|
|||
}, function (error) {
|
||||
res.json(401, {error: error.message});
|
||||
});
|
||||
} else {
|
||||
res.json(401, {error: 'Slow down, there are way too many login attempts!'});
|
||||
}
|
||||
|
||||
},
|
||||
changepw: function (req, res) {
|
||||
api.users.changePassword({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*globals casper, __utils__, url, user */
|
||||
/*globals casper, __utils__, url, user, falseUser */
|
||||
casper.test.begin("Ghost admin will load login page", 2, function suite(test) {
|
||||
|
||||
casper.test.filename = "admin_test.png";
|
||||
|
@ -45,3 +45,38 @@ casper.test.begin("Can login to Ghost", 3, function suite(test) {
|
|||
test.done();
|
||||
});
|
||||
});
|
||||
|
||||
casper.test.begin("Can't spam it", 2, function suite(test) {
|
||||
|
||||
casper.test.filename = "login_test.png";
|
||||
|
||||
casper.start(url + "ghost/login/", function testTitle() {
|
||||
test.assertTitle("", "Ghost admin has no title");
|
||||
}).viewport(1280, 1024);
|
||||
|
||||
casper.waitFor(function checkOpaque() {
|
||||
return this.evaluate(function () {
|
||||
var loginBox = document.querySelector('.login-box');
|
||||
return window.getComputedStyle(loginBox).getPropertyValue('display') === "block"
|
||||
&& window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1";
|
||||
});
|
||||
}, function then() {
|
||||
this.fill("#login", falseUser, true);
|
||||
casper.wait(200, function doneWait() {
|
||||
this.fill("#login", falseUser, true);
|
||||
});
|
||||
|
||||
});
|
||||
casper.wait(200, function doneWait() {
|
||||
this.echo("I've waited for 1 seconds.");
|
||||
});
|
||||
|
||||
casper.then(function testForErrorMessage() {
|
||||
test.assertSelectorHasText('.notification-error', 'Slow down, there are way too many login attempts!');
|
||||
});
|
||||
|
||||
casper.run(function () {
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ var host = casper.cli.options.host || 'localhost',
|
|||
email: email,
|
||||
password: password
|
||||
},
|
||||
falseUser = {
|
||||
email: email,
|
||||
password: 'letmethrough'
|
||||
},
|
||||
testPost = {
|
||||
title: "Bacon ipsum dolor sit amet",
|
||||
content: "I am a test post.\n#I have some small content"
|
||||
|
|
Loading…
Reference in a new issue