express-brute = function (store, options) { var i; ExpressBrute.instanceCount++; this.name = "brute"+ExpressBrute.instanceCount; _.bindAll(this, 'reset', 'getMiddleware'); // set options this.options = _.extend({}, ExpressBrute.defaults, options); if (this.options.minWait < 1) { this.options.minWait = 1; } this.store = store; // build delays array this.delays = [this.options.minWait]; while(this.delays[this.delays.length-1] < this.options.maxWait) { var nextNum = this.delays[this.delays.length-1] + (this.delays.length > 1 ? this.delays[this.delays.length-2] : 0); this.delays.push(nextNum); } this.delays[this.delays.length-1] = this.options.maxWait; // set default lifetime if (typeof this.options.lifetime == "undefined") { this.options.lifetime = (this.options.maxWait/1000)*(this.delays.length + this.options.freeRetries); this.options.lifetime = Math.ceil(this.options.lifetime); } // generate "prevent" middleware this.prevent = this.getMiddleware(); }
n/a
AbstractClientStore = function () { }
n/a
FailForbidden = function (req, res, next, nextValidRequestDate) { setRetryAfter(res, nextValidRequestDate); res.status(403); res.send({error: {text: "Too many requests in this time frame.", nextValidRequestDate: nextValidRequestDate}}); }
n/a
FailMark = function (req, res, next, nextValidRequestDate) { res.status(429); setRetryAfter(res, nextValidRequestDate); res.nextValidRequestDate = nextValidRequestDate; next(); }
n/a
FailTooManyRequests = function (req, res, next, nextValidRequestDate) { setRetryAfter(res, nextValidRequestDate); res.status(429); res.send({error: {text: "Too many requests in this time frame.", nextValidRequestDate: nextValidRequestDate}}); }
n/a
MemoryStore = function (options) { this.data = {}; _.bindAll(this, 'set', 'get', 'reset'); this.options = _.extend({}, MemoryStore.defaults, options); }
...
$ npm install express-brute
A Simple Example
----------------
``` js
var ExpressBrute = require('express-brute');
var store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this
in production
var bruteforce = new ExpressBrute(store);
app.post('/auth',
bruteforce.prevent, // error 429 if we hit this route too often
function (req, res, next) {
res.send('Success!');
}
...
_getKey = function (arr) { var key = ''; _(arr).each(function (part) { if (part) { key += crypto.createHash('sha256').update(part).digest('base64'); } }); return crypto.createHash('sha256').update(key).digest('base64'); }
...
return typeof options.failCallback === 'undefined' ? this.options.failCallback : options.failCallback;
}, this);
// create middleware
return _.bind(function (req, res, next) {
keyFunc(req, res, _.bind(function (key) {
if(!options.ignoreIP) {
key = ExpressBrute._getKey([req.ip, this.name, key]);
} else {
key = ExpressBrute._getKey([this.name, key]);
}
// attach a simpler "reset" function to req.brute.reset
if (this.options.attachResetToRequest) {
var reset = _.bind(function (callback) {
...
index = function (store, options) { var i; ExpressBrute.instanceCount++; this.name = "brute"+ExpressBrute.instanceCount; _.bindAll(this, 'reset', 'getMiddleware'); // set options this.options = _.extend({}, ExpressBrute.defaults, options); if (this.options.minWait < 1) { this.options.minWait = 1; } this.store = store; // build delays array this.delays = [this.options.minWait]; while(this.delays[this.delays.length-1] < this.options.maxWait) { var nextNum = this.delays[this.delays.length-1] + (this.delays.length > 1 ? this.delays[this.delays.length-2] : 0); this.delays.push(nextNum); } this.delays[this.delays.length-1] = this.options.maxWait; // set default lifetime if (typeof this.options.lifetime == "undefined") { this.options.lifetime = (this.options.maxWait/1000)*(this.delays.length + this.options.freeRetries); this.options.lifetime = Math.ceil(this.options.lifetime); } // generate "prevent" middleware this.prevent = this.getMiddleware(); }
n/a
AbstractClientStore = function () { }
n/a
increment = function (key, lifetime, callback) { var self = this; this.get(key, function (err, value) { if (err) { callback(err); } else { var count = value ? value.count+1 : 1; self.set(key, {count: count, lastRequest: new Date(), firstRequest: new Date()}, lifetime, function (err) { var prevValue = { count: value ? value.count : 0, lastRequest: value ? value.lastRequest : null, firstRequest: value ? value.firstRequest : null }; typeof callback == 'function' && callback(err, prevValue); }); } }); }
n/a
MemoryStore = function (options) { this.data = {}; _.bindAll(this, 'set', 'get', 'reset'); this.options = _.extend({}, MemoryStore.defaults, options); }
...
$ npm install express-brute
A Simple Example
----------------
``` js
var ExpressBrute = require('express-brute');
var store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this
in production
var bruteforce = new ExpressBrute(store);
app.post('/auth',
bruteforce.prevent, // error 429 if we hit this route too often
function (req, res, next) {
res.send('Success!');
}
...
get = function (key, callback) { key = this.options.prefix+key; var data = this.data[key] && this.data[key].value; if (data) { data = JSON.parse(data); data.lastRequest = new Date(data.lastRequest); data.firstRequest = new Date(data.firstRequest); } typeof callback == 'function' && callback(null, data); }
...
req.brute = {
reset: reset
};
}
// filter request
this.store.get(key, _.bind(function (err, value) {
if (err) {
this.options.handleStoreError({
req: req,
res: res,
next: next,
message: "Cannot get request count",
parent: err
...
reset = function (key, callback) { key = this.options.prefix+key; if (this.data[key] && this.data[key].timeout) { longTimeout.clearTimeout(this.data[key].timeout); } delete this.data[key]; typeof callback == 'function' && callback(null); }
...
} else {
key = ExpressBrute._getKey([this.name, key]);
}
// attach a simpler "reset" function to req.brute.reset
if (this.options.attachResetToRequest) {
var reset = _.bind(function (callback) {
this.store.reset(key, function (err) {
if (typeof callback == 'function') {
process.nextTick(function () {
callback(err);
});
}
});
}, this);
...
set = function (key, value, lifetime, callback) { key = this.options.prefix+key; lifetime = lifetime || 0; value = JSON.stringify(value); if (!this.data[key]) { this.data[key] = {}; } else if (this.data[key].timeout) { longTimeout.clearTimeout(this.data[key].timeout); } this.data[key].value = value; if (lifetime) { this.data[key].timeout = longTimeout.setTimeout(_.bind(function () { delete this.data[key]; }, this), 1000*lifetime); } typeof callback == 'function' && callback(null); }
...
delay = 0;
nextValidRequestTime = firstRequestTime = lastValidRequestTime = this.now();
remainingLifetime = this.options.lifetime || 0;
}
}
if (nextValidRequestTime <= this.now() || count <= this.options.freeRetries) {
this.store.set(key, {
count: count+1,
lastRequest: new Date(this.now()),
firstRequest: new Date(firstRequestTime)
}, remainingLifetime, _.bind(function (err) {
if (err) {
this.options.handleStoreError({
req: req,
...
failCallback = function (req, res, next, nextValidRequestDate) { setRetryAfter(res, nextValidRequestDate); res.status(429); res.send({error: {text: "Too many requests in this time frame.", nextValidRequestDate: nextValidRequestDate}}); }
n/a
handleStoreError = function (err) { throw { message: err.message, parent: err.parent }; }
...
};
}
// filter request
this.store.get(key, _.bind(function (err, value) {
if (err) {
this.options.handleStoreError({
req: req,
res: res,
next: next,
message: "Cannot get request count",
parent: err
});
return;
...
index = function (store, options) { var i; ExpressBrute.instanceCount++; this.name = "brute"+ExpressBrute.instanceCount; _.bindAll(this, 'reset', 'getMiddleware'); // set options this.options = _.extend({}, ExpressBrute.defaults, options); if (this.options.minWait < 1) { this.options.minWait = 1; } this.store = store; // build delays array this.delays = [this.options.minWait]; while(this.delays[this.delays.length-1] < this.options.maxWait) { var nextNum = this.delays[this.delays.length-1] + (this.delays.length > 1 ? this.delays[this.delays.length-2] : 0); this.delays.push(nextNum); } this.delays[this.delays.length-1] = this.options.maxWait; // set default lifetime if (typeof this.options.lifetime == "undefined") { this.options.lifetime = (this.options.maxWait/1000)*(this.delays.length + this.options.freeRetries); this.options.lifetime = Math.ceil(this.options.lifetime); } // generate "prevent" middleware this.prevent = this.getMiddleware(); }
n/a
FailForbidden = function (req, res, next, nextValidRequestDate) { setRetryAfter(res, nextValidRequestDate); res.status(403); res.send({error: {text: "Too many requests in this time frame.", nextValidRequestDate: nextValidRequestDate}}); }
n/a
FailMark = function (req, res, next, nextValidRequestDate) { res.status(429); setRetryAfter(res, nextValidRequestDate); res.nextValidRequestDate = nextValidRequestDate; next(); }
n/a
FailTooManyRequests = function (req, res, next, nextValidRequestDate) { setRetryAfter(res, nextValidRequestDate); res.status(429); res.send({error: {text: "Too many requests in this time frame.", nextValidRequestDate: nextValidRequestDate}}); }
n/a
MemoryStore = function (options) { this.data = {}; _.bindAll(this, 'set', 'get', 'reset'); this.options = _.extend({}, MemoryStore.defaults, options); }
...
$ npm install express-brute
A Simple Example
----------------
``` js
var ExpressBrute = require('express-brute');
var store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this
in production
var bruteforce = new ExpressBrute(store);
app.post('/auth',
bruteforce.prevent, // error 429 if we hit this route too often
function (req, res, next) {
res.send('Success!');
}
...
_getKey = function (arr) { var key = ''; _(arr).each(function (part) { if (part) { key += crypto.createHash('sha256').update(part).digest('base64'); } }); return crypto.createHash('sha256').update(key).digest('base64'); }
...
return typeof options.failCallback === 'undefined' ? this.options.failCallback : options.failCallback;
}, this);
// create middleware
return _.bind(function (req, res, next) {
keyFunc(req, res, _.bind(function (key) {
if(!options.ignoreIP) {
key = ExpressBrute._getKey([req.ip, this.name, key]);
} else {
key = ExpressBrute._getKey([this.name, key]);
}
// attach a simpler "reset" function to req.brute.reset
if (this.options.attachResetToRequest) {
var reset = _.bind(function (callback) {
...
getMiddleware = function (options) { // standardize input options = _.extend({}, options); var keyFunc = options.key; if (typeof keyFunc !== 'function') { keyFunc = function (req, res, next) { next(options.key); }; } var getFailCallback = _.bind(function () { return typeof options.failCallback === 'undefined' ? this.options.failCallback : options.failCallback; }, this); // create middleware return _.bind(function (req, res, next) { keyFunc(req, res, _.bind(function (key) { if(!options.ignoreIP) { key = ExpressBrute._getKey([req.ip, this.name, key]); } else { key = ExpressBrute._getKey([this.name, key]); } // attach a simpler "reset" function to req.brute.reset if (this.options.attachResetToRequest) { var reset = _.bind(function (callback) { this.store.reset(key, function (err) { if (typeof callback == 'function') { process.nextTick(function () { callback(err); }); } }); }, this); if (req.brute && req.brute.reset) { // wrap existing reset if one exists var oldReset = req.brute.reset; var newReset = reset; reset = function (callback) { oldReset(function () { newReset(callback); }); }; } req.brute = { reset: reset }; } // filter request this.store.get(key, _.bind(function (err, value) { if (err) { this.options.handleStoreError({ req: req, res: res, next: next, message: "Cannot get request count", parent: err }); return; } var count = 0, delay = 0, lastValidRequestTime = this.now(), firstRequestTime = lastValidRequestTime; if (value) { count = value.count; lastValidRequestTime = value.lastRequest.getTime(); firstRequestTime = value.firstRequest.getTime(); var delayIndex = value.count - this.options.freeRetries - 1; if (delayIndex >= 0) { if (delayIndex < this.delays.length) { delay = this.delays[delayIndex]; } else { delay = this.options.maxWait; } } } var nextValidRequestTime = lastValidRequestTime+delay, remainingLifetime = this.options.lifetime || 0; if (!this.options.refreshTimeoutOnRequest && remainingLifetime > 0) { remainingLifetime = remainingLifetime - Math.floor((this.now() - firstRequestTime) / 1000); if (remainingLifetime < 1) { // it should be expired alredy, treat this as a new request and reset everything count = 0; delay = 0; nextValidRequestTime = firstRequestTime = lastValidRequestTime = this.now(); remainingLifetime = this.options.lifetime || 0; } } if (nextValidRequestTime <= this.now() || count <= this.options.freeRetries) { this.store.set(key, { count: count+1, lastRequest: new Date(this.now()), firstRequest: new Date(firstRequestTime) }, remainingLifetime, _.bind(function (err) { if (err) { this.options.handleStoreError({ req: req, res: res, next: next, message: "Cannot increment request count", parent: err }); return; } typeof next == 'function' && next(); },this)); } else { var failCallback = getFailCallback(); typeof failCallback === 'function' && failCallback(req, res, next, new Date(nextValidRequestTime)); } }, this)); },this)); }, this); }
...
// set default lifetime
if (typeof this.options.lifetime == "undefined") {
this.options.lifetime = (this.options.maxWait/1000)*(this.delays.length + this.options.freeRetries);
this.options.lifetime = Math.ceil(this.options.lifetime);
}
// generate "prevent" middleware
this.prevent = this.getMiddleware();
};
ExpressBrute.prototype.getMiddleware = function (options) {
// standardize input
options = _.extend({}, options);
var keyFunc = options.key;
if (typeof keyFunc !== 'function') {
keyFunc = function (req, res, next) { next(options.key); };
...
now = function () { return Date.now(); }
...
parent: err
});
return;
}
var count = 0,
delay = 0,
lastValidRequestTime = this.now(),
firstRequestTime = lastValidRequestTime;
if (value) {
count = value.count;
lastValidRequestTime = value.lastRequest.getTime();
firstRequestTime = value.firstRequest.getTime();
var delayIndex = value.count - this.options.freeRetries - 1;
...
reset = function (ip, key, callback) { key = ExpressBrute._getKey([ip, this.name, key]); this.store.reset(key, _.bind(function (err) { if (err) { this.options.handleStoreError({ message: "Cannot reset request count", parent: err, key: key, ip: ip }); } else { if (typeof callback == 'function') { process.nextTick(_.bind(function () { callback.apply(this, arguments); }, this)); } } },this)); }
...
} else {
key = ExpressBrute._getKey([this.name, key]);
}
// attach a simpler "reset" function to req.brute.reset
if (this.options.attachResetToRequest) {
var reset = _.bind(function (callback) {
this.store.reset(key, function (err) {
if (typeof callback == 'function') {
process.nextTick(function () {
callback(err);
});
}
});
}, this);
...