Giter Club home page Giter Club logo

express-brute's Introduction

express-brute

NPM Version NPM Downloads Build Status Coverage Status Dependency Status

A brute-force protection middleware for express routes that rate-limits incoming requests, increasing the delay with each request in a fibonacci-like sequence.

Installation

via npm:

  $ npm install express-brute

A Simple Example

var ExpressBrute = require('express-brute');

// stores state locally, don't use this in production
var store = new ExpressBrute.MemoryStore();
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!');
	}
);

Classes

ExpressBrute(store, options)

  • store An instance of ExpressBrute.MemoryStore or some other ExpressBrute store (see a list of known stores below).
  • options
    • freeRetries The number of retries the user has before they need to start waiting (default: 2)
    • minWait The initial wait time (in milliseconds) after the user runs out of retries (default: 500 milliseconds)
    • maxWait The maximum amount of time (in milliseconds) between requests the user needs to wait (default: 15 minutes). The wait for a given request is determined by adding the time the user needed to wait for the previous two requests.
    • lifetime The length of time (in seconds since the last request) to remember the number of requests that have been made by an IP. By default it will be set to maxWait * the number of attempts before you hit maxWait to discourage simply waiting for the lifetime to expire before resuming an attack. With default values this is about 6 hours.
    • failCallback Gets called with (req, resp, next, nextValidRequestDate) when a request is rejected (default: ExpressBrute.FailForbidden)
    • attachResetToRequest Specify whether or not a simplified reset method should be attached at req.brute.reset. The simplified method takes only a callback, and resets all ExpressBrute middleware that was called on the current request. If multiple instances of ExpressBrute have middleware on the same request, only those with attachResetToRequest set to true will be reset (default: true)
    • refreshTimeoutOnRequest Defines whether the lifetime counts from the time of the last request that ExpressBrute didn't prevent for a given IP (true) or from of that IP's first request (false). Useful for allowing limits over fixed periods of time, for example: a limited number of requests per day. (Default: true). More info
    • handleStoreError Gets called whenever an error occurs with the persistent store from which ExpressBrute cannot recover. It is passed an object containing the properties message (a description of the message), parent (the error raised by the session store), and [key, ip] or [req, res, next] depending on whether or the error occurs during reset or in the middleware itself.

ExpressBrute.MemoryStore()

An in-memory store for persisting request counts. Don't use this in production, instead choose one of the more robust store implementations listed below.

ExpressBrute Instance Methods

  • prevent(req, res, next) Middleware that will bounce requests that happen faster than the current wait time by calling failCallback. Equivilent to getMiddleware(null)
  • getMiddleware(options) Generates middleware that will bounce requests with the same key and IP address that happen faster than the current wait time by calling failCallback. Also attaches a function at req.brute.reset that can be called to reset the counter for the current ip and key. This functions as the reset instance method, but without the need to explicitly pass the ip and key paramters
    • key can be a string or alternatively it can be a function(req, res, next) that calls next, passing a string as the first parameter.
    • failCallback Allows you to override the value of failCallback for this middleware
    • ignoreIP Disregard IP address when matching requests if set to true. Defaults to false.
  • reset(ip, key, next) Resets the wait time between requests back to its initial value. You can pass null for key if you want to reset a request protected by prevent.

Built-in Failure Callbacks

There are some built-in callbacks that come with BruteExpress that handle some common use cases.

  • ExpressBrute.FailTooManyRequests Terminates the request and responses with a 429 (Too Many Requests) error that has a Retry-After header and a JSON error message.
  • ExpressBrute.FailForbidden Terminates the request and responds with a 403 (Forbidden) error that has a Retry-After header and a JSON error message. This is provided for compatibility with ExpressBrute versions prior to v0.5.0, for new users FailTooManyRequests is the preferred behavior.
  • ExpressBrute.FailMark Sets res.nextValidRequestDate, the Retry-After header and the res.status=429, then calls next() to pass the request on to the appropriate routes.

ExpressBrute stores

There are a number adapters that have been written to allow ExpressBrute to be used with different persistent storage implementations, some of the ones I know about include:

If you write your own store and want me to add it to the list, just drop me an email or create an issue.

A More Complex Example

require('connect-flash');
var ExpressBrute = require('express-brute'),
	MemcachedStore = require('express-brute-memcached'),
	moment = require('moment'),
    store;

if (config.environment == 'development'){
	store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this in production
} else {
	// stores state with memcached
	store = new MemcachedStore(['127.0.0.1'], {
		prefix: 'NoConflicts'
	});
}

var failCallback = function (req, res, next, nextValidRequestDate) {
	req.flash('error', "You've made too many failed attempts in a short period of time, please try again "+moment(nextValidRequestDate).fromNow());
	res.redirect('/login'); // brute force protection triggered, send them back to the login page
};
var handleStoreError = function (error) {
	log.error(error); // log this error so we can figure out what went wrong
	// cause node to exit, hopefully restarting the process fixes the problem
	throw {
		message: error.message,
		parent: error.parent
	};
}
// Start slowing requests after 5 failed attempts to do something for the same user
var userBruteforce = new ExpressBrute(store, {
	freeRetries: 5,
	minWait: 5*60*1000, // 5 minutes
	maxWait: 60*60*1000, // 1 hour,
	failCallback: failCallback,
	handleStoreError: handleStoreError
});
// No more than 1000 login attempts per day per IP
var globalBruteforce = new ExpressBrute(store, {
	freeRetries: 1000,
	attachResetToRequest: false,
	refreshTimeoutOnRequest: false,
	minWait: 25*60*60*1000, // 1 day 1 hour (should never reach this wait time)
	maxWait: 25*60*60*1000, // 1 day 1 hour (should never reach this wait time)
	lifetime: 24*60*60, // 1 day (seconds not milliseconds)
	failCallback: failCallback,
	handleStoreError: handleStoreError
});

app.set('trust proxy', 1); // Don't set to "true", it's not secure. Make sure it matches your environment
app.post('/auth',
	globalBruteforce.prevent,
	userBruteforce.getMiddleware({
		key: function(req, res, next) {
			// prevent too many attempts for the same username
			next(req.body.username);
		}
	}),
	function (req, res, next) {
		if (User.isValidLogin(req.body.username, req.body.password)) { // omitted for the sake of conciseness
		 	// reset the failure counter so next time they log in they get 5 tries again before the delays kick in
			req.brute.reset(function () {
				res.redirect('/'); // logged in, send them to the home page
			});
		} else {
			res.flash('error', "Invalid username or password")
			res.redirect('/login'); // bad username/password, send them back to the login page
		}
	}
);

Behind Proxy Servers

If your application is behind a proxy (Apache, Nginx, load balancer, CDN, etc) you should not forget set the trust proxy param as appropriate for your Express application. For example:

app.set('trust proxy', 1);

Please note: don't use the value true because it tells express to trust the whole X-Forwarded-For chain, which could allow an attacker to bypass the express brute protections by spoofing source ips. The easiest solution is probably to set your proxy depth appropriately, but for more information on other options see Express' behind proxies guide

Changelog

v1.0.1

  • BUG: Fixed an edge case where freeretries weren't being respected if app servers had slightly different times

v1.0.0

  • NEW: Updated to use Express 4.x as a peer dependency.
  • REMOVED: proxyDepth option on ExpressBrute has been removed. Use app.set('trust proxy', x) from Express 4 instead. More Info
  • REMOVED: getIPFromRequest(req) has been removed from instances, use req.ip instead.

v0.6.0

  • NEW: Added new ignoreIP option. (Thanks Magnitus-!)
  • CHANGED: .reset callbacks are now always called asyncronously, regardless of the implementation of the store (particularly effects MemoryStore).
  • CHANGED: Unit tests have been converted from Jasmine to Mocha/Chai/Sinon
  • BUG: Fixed a crash when .reset was called without a callback function

v0.5.3

  • NEW: Added the handleStoreError option to allow more customizable handling of errors that are thrown by the persistent store. Default behavior is to throw the errors as an exception - there is nothing ExpressBrute can do to recover.
  • CHANGED: Errors thrown as a result of errors raised by the store now include the store's error as well, for debugging purposes.

v0.5.2

  • CHANGED: Stopped using res.send(status, body), as it is deprecated in express 4.x. Instead call res.status and res.send separately (Thanks marinewater!)

v0.5.1

  • BUG: When setting proxyDepth to 1, ips is never populated with proxied X-Forwarded-For IP.

v0.5.0

  • NEW: Added an additional FailTooManyRequests failure callback, that returns a 429 (TooManyRequests) error instead of 403 (Forbidden). This is a more accurate error status code.
  • NEW: All the built in failure callbacks now set the "Retry-After" header to the number of seconds until it is safe to try again. Per RFC6585
  • NEW: Documentation updated to list some known store implementations.
  • CHANGED: Default failure callback is now FailTooManyRequests. FailForbidden remains an option for backwards compatiblity.
  • CHANGED: ExpressBrute.MemcachedStore is no longer included by default, and is now available as a separate module (because there are multiple store options it doesn't really make sense to include one by default).
  • CHANGED: FailMark no longer sets returns 403 Forbidden, instead does 429 TooManyRequets.

v0.4.2

  • BUG: In some cases when no callbacks were supplied memcached would drop the request. Ensure that memcached always sees a callback even if ExpressBrute isn't given one.

v0.4.1

  • NEW: refreshTimeoutOnRequest option that allows you to prevent the remaining lifetime for a timer from being reset on each request (useful for implementing limits for set time frames, e.g. requests per day)
  • BUG: Lifetimes were not previously getting extended properly for instances of ExpressBrute.MemoryStore

v0.4.0

  • NEW: attachResetToRequest parameter that lets you prevent the request object being decorated
  • NEW: failCallback can be overriden by getMiddleware
  • NEW: proxyDepth option on ExpressBrute that specifies how many levels of the X-Forwarded-For header to trust (inspired by express-bouncer).
  • NEW: getIPFromRequest method that essentially allows reset to used in a similar ways as in v0.2.2. This also respects the new proxyDepth setting.
  • CHANGED: getMiddleware now takes an options object instead of the key directly.

v0.3.0

  • NEW: Support for using custom keys to group requests further (e.g. grouping login requests by username)
  • NEW: Support for middleware from multiple instances of ExpressBrute on the same route.
  • NEW: Tracking lifetime now has a reasonable default derived from the other settings for that instance of ExpressBrute
  • NEW: Keys are now hashed before saving to a store, to prevent really long key names and reduce the possibility of collisions.
  • NEW: There is now a convience method that gets attached to req object as req.brute.reset. It takes a single parameter (a callback), and will reset all the counters used by ExpressBrute middleware that was called for the current route.
  • CHANGED: Tracking lifetime is now specified on ExpressBrute instead of MemcachedStore. This also means lifetime is now supported by MemoryStore.
  • CHANGED: The function signature for ExpressBrute.reset has changed. It now requires an IP and key be passed instead of a request object.
  • IMPROVED: Efficiency for large values of freeRetries.
  • BUG: Removed a small chance of incorrectly triggering brute force protection.

express-brute's People

Contributors

adampflug avatar carsongee avatar cbargren avatar dring1 avatar fhemberger avatar fpereira1 avatar goloroden avatar gswalden avatar idangozlan avatar jfromaniello avatar kittygiraudel avatar magnitus- avatar marinewater avatar natsukagami avatar ngpixel avatar redwood-egarcea avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

express-brute's Issues

Make getMiddleware able to read status of other route

Hi,

I was using this awesome plugin. I have a query regarding this. can we check the status of another route in getMiddleware.
for eg if I want to check the status in 'GET /login' that 'POST /login' is facing an attack and get blocked.

When lifetime is reset with refreshTimeoutOnRequest?

First of all, thanks for the great module!

From reading the documentation, I'm not very clear when exactly the lifetime is reset with refreshTimeoutOnRequest set to true.

Is it once the lifetime ends a new one starts then?

Few questions on status / latest version?

I'm looking at using this module in several projects, and wanted to check in beforehand 👋

Is this module still maintained and/or used in production by anyone? It seems like this is the most established set of middleware for brute force prevention, or is there an alternative I'm missing?

Also, which is the correct/latest version to use?

I'm a little confused as, in the readme it has some documentation about v1.0.0, but the latest version on npm is 0.7.0-beta.0 which was published 2 months ago. Is the beta safe to use?

Finally, I noticed this module depends on underscore, would there be any interest in switching to lodash as that is more common nowadays in my experience. Not a big deal, just wondering 🤔

Thanks for reading! :)

error handling for store.get

I'm using express-brute with express-brute-mongo. I see occasions when the mongoDB call returns an error (possibly a timeout). This then doesn't get handled smoothly in express-brute/index.js L83-85 and crashes my node instance. I think this should at least pass on the actual error for debugging purposes

this.store.get(key, _.bind(function (err, value) {
  if (err) {
    throw "Cannot get request count";
  }

Middleware skipping

Hi there. First off I'd like to say that this package has been a site saver for sure and a great big thank you!

On to the nitty gritty.

I've done my best to tailor this to the coding style in the examples here so please forgive me if there's a typo or something I am just plain missing.

We've been utilizing this package like such for about a year with current express v4.x and current LTS nodejs with:

app.route('/test').get(userBruteforce.getMiddleware({ key: someKeyedNewExpressBruteFunction }), doSomethingFunction);

... and it's been working fantastic so far.

What we need to do is something like this:

app.route('/test').get(function (req, res, next) {
  userBruteforce.getMiddleware({ key: someKeyedNewExpressBruteFunction })(req, res, next);
}, doSomethingFunction);

... which seems to work well too... however what we really need is something like this...

app.route('/test').get(function (req, res, next) {
  if (someVar) {
    userBruteforce.getMiddleware({ key: someKeyedNewExpressBruteFunction })(req, res, next);
  } else {
    next(); // or use a different ExpressBrute middleware instance
  }
}, doSomethingFunction);

... when this is done express-brute becomes unstable especially during the first few gets and doesn't seem to behave the same when the someVar conditional doesn't exist.

Some general questions after reading a lot of places on the internet including stackoverflow, the README.md, and testing with the new ExpressBrute.MemoryStore for hours:

  • Are we doing the middleware hopping incorrectly in any of these three code snippet examples? ❓
  • It seems that putting conditional code on example three blows things up (e.g. no more protection to start) but it appears to work on example two... again is there something up? ❓ e.g. what is being missed here? ❓ Did I miss the boat? 😉 😄

Thank you so much for a moment of your time.

"Too many requests" for successful logins

Hi,

I'm using this middleware for my login API, I noticed that it blocks me after X successful login.
is there a way to reset the count after a successful login?

Thanks.

getMiddleware options are different than constructor options

From what I can see, unless you're generating your own prevent-esque middleware function, the more complex example won't actually use all of the options.

Here's a condensed version of the code I see:

var ExpressBrute = module.exports = function (store, options) {
    ** OMITTED EXTRANEOUS SETUP CODE  **

    // 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); };
    }
    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([this.getIPFromRequest(req), this.name, key]);
            } else {
                key = ExpressBrute._getKey([this.name, key]);
            }

While getMiddleware() can take an options parameter, it never is passed one in the constructor unless someone decides to call it directly. The constructor right now passes nothing and sometimes the getMiddleware() method uses this.options[property], which is fine, but for cases like ignoreIP, it's trying to get it from the options object (options.ignoreIP) which is always undefined.

I'm working on a PR to fix the unnecessary passing of a parameter and adding the necessary tests.
EDIT: I see this isn't unnecessary and is by design.

Retrieve number of remaining requests

Hi,

I'm using express-brute as a rate limiter for my API, allowing for example a maximum of 3600 requests per IP per hour. I would like to give some information regarding rate limit through headers on every response (even those that are not rate limited), especially the following ones:

  • RateLimit-Count: number of requests already received from the beginning of the time frame.
  • RateLimit-Reset: date when the time frame is reset. I know there is a 'nextValidRequestDate' argument provided in 'failCallback', but I can't see any way to get that information from a successful request.

Is there a way to do it?

PS: I'm using the in-memory store at the moment.

[freeRetries] strange behavior

Hello,

thanks for that great module!

However I am facing a strange behavior regarding the number of freeRetries.

Indeed my snippet of code is the following:

options = { freeRetries: 10,
            minWait: 25*60*60*1000,
            maxWait: 25*60*60*1000,
            proxyDepth: 1,
            failCallback: function(req, res, next, nextValidRequestDate) {
              res.status(429).send('You have made too many failed attempts in a short period of time, please try again ' + moment(nextValidRequestDate).fromNow());
            }},
bruteforce = new ExpressBrute(bruteRedisStore, options);

In that case what happen:

After only 5 requests (whatever the time between each) I got:
You have made too many failed attempts in a short period of time, please try again in a few seconds

A retry passed few second still does not reset the "counter".

I was expecting to reach 10 -> banned -> retry after ~10 sec -> OK -> retry fail etc.... with gradually increasing waiting time.

I might not have understood exactly the behavior of the module I think. If you got some time to explain me what is wrong in my thinking I would be very thankful!

Cheers!

add new express-brute-couchbase store

Hi,
I realized a new store modules for express-brute on couchbase store.
Thank you for adding it in your docs.
You made a great job !

https://www.npmjs.com/package/express-brute-couchbase

performance concern

Thanks for creating this great plugin.

Just wondering that by adding this middleware to express, what would be the affect of the request handling performance? I mean, is it going to delay the speed of server side working? Thanks.

app.use() all routes

I can't seem to find anywhere in the documentation if you can use this for all routes by going app.use(require('express-brute)());

I really don't feel like inserting this into each route - I have hundreds..

Is this possible?

Is there a way to retrieve the IP

Hello and thanks for this module,

I'm wondering if there is a way to get the IP and put it in the database next to the other data?

I'm using the express-brute-mongoose store with this schema (if that's relevant) :

var bruteForceSchema = mongoose.Schema({
_id: { type: String, index: 1 },
data: {
    count: Number,
    lastRequest: Date,
    firstRequest: Date
},
expires: { type: Date, index: { expires: '1d' } }
});

whitelist request with specific tokens

Hello there,
I would like to know how can I protect one route that can be used with an API key (in the request parameters) without any limits and with no API key where I want to apply limits.

Cheers.

[Java] E11000 duplicate key error collection in mongodb

Getting below exception while inserting multiple documents in mongo db using list.
com.mongodb.MongoBulkWriteException: Bulk write operation error on server papaya-b.corp.adobe.com:27031. Write errors: [BulkWriteError{index=0, code=11000, message='E11000 duplicate key error collection: HELPXDB.performance_author index: id dup key: { : ObjectId('5a290d23af4cd538dc2106fc') }', details={ }}].

I have created one test method , in which I have given data provider having multiple entries.
For one entry there are multiple documents , So I have created a list
static ArrayList testRecords = new ArrayList();
and added the documents to it .

In aftertest method , I am trying to insert all records using list in db.
coll.insertMany(docs);

While my test execute for first entry of dataprovider , it inserts the data in db successfully but for second onwards entry , Its giving the bulk write operation error.

MongoDb version used : 3.2

Please help to get this issue fixed.

What data is stored?

Firstly, express-brute is awesome!

Secondly, potentially stupid question, but I wanted to confirm what data express-brute stores. Is it just ip/timestamp or is it the entire request/parameters?

My concern is that if I put this in front of a PCI/PII/login related endpoint, I could be exposing sensitive information in my environment.

Not a big deal if I it is, I'm assuming if it does, should I just remove extraneous info and hash potentially relevant sensitive data?

Memory Store breaks with lifetimes over 24.8 Days

Longer than that and you roll over the 32bit integer and the setTimeouts execute immediately (clearing brute force prevention, which is probably bad). There should probably be a range check and an error thrown in this case (if we can do it while initializing the store, otherwise it should probably just cap it at 24.8 days).

Alternatively we could use something like long-timeout... https://www.npmjs.com/package/long-timeout

New release planned?

Would be nice to have a 0.5.4 up on npm with ignoreIp and other post 0.5.3 changes

Greater Customisation

First of all, thanks for developing and maintaining this library. It is great.

Currently, there are 2 limits to the library for my use-case:

  1. Fixation on IP

I would be nice if the function 'getIPFromRequest' was renamed 'getKeyFromRequest', defaulted to 'getIPFromRequest', but could be customisable via a constructor option.

For example, in a route that requires a user to be logged in, a requester's user ID, rather than IP might be a more reliable metric to identify him.

  1. Separation Between Incrementer and Verifier

Would be great if, optionally, the routes to increment the counter and verify the counter for a brute force attack could be separated.

This would allow you to do something like this:

  • Check for brute force on login attempt
  • If login failed, then increment access count

Segmentation fault (core dumped)

If you can tell me what likely caused this I can try to fix it myself and do a pull request. I do not know what the error in the title actually means.

Relevant code:

var store = new MongoStore(function (ready) {
MongoClient.connect('mongodb://localhost/globlee', function(err, db) {
if (err){
console.log(err);
} else{
ready(db.collection('bruteforce-store-vendor'));
}
});
});

var failCallback = function (req, res, next, nextValidRequestDate) {
res.send("Too many failed attemps. Try again in: "+moment(nextValidRequestDate).fromNow());
};
var handleStoreError = function (error) {
console.log.error(error);
}
var userBruteforce = new ExpressBrute(store, {
freeRetries: 5,
proxyDepth: 1,
minWait: 5_60_1000, // 5 minutes
maxWait: 60_60_1000, // 1 hour,
failCallback: failCallback,
handleStoreError: handleStoreError
});

var globalBruteforce = new ExpressBrute(store, {
freeRetries: 1000,
proxyDepth: 1,
attachResetToRequest: false,
refreshTimeoutOnRequest: false,
minWait: 25_60_60_1000, // 1 day 1 hour (should never reach this wait time)
maxWait: 25_60_60_1000, // 1 day 1 hour (should never reach this wait time)
lifetime: 24_60_60, // 1 day (seconds not milliseconds)
failCallback: failCallback,
handleStoreError: handleStoreError
});

router.post('/v/login', globalBruteforce.prevent,
userBruteforce.getMiddleware({
key: function(req, res, next) {
next(req.body.website);
}
}), login, function(req,res){
console.log("this was called!!") //happens after login middleware completes
req.brute.reset(function () {
res.redirect('/v');
//return next();
});
});

screenshot 17
screenshot 16

If you would like me to use a debug tool like gnu debugger, I can do so.

debug log:
0 info it worked if it ends with ok
1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'prestart', 'start', 'poststart' ]
5 info prestart [email protected]
6 info start [email protected]
7 verbose unsafe-perm in lifecycle true
8 info [email protected] Failed to exec start script
9 verbose stack Error: [email protected] start: node ./bin/www
9 verbose stack Exit status 139
9 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:213:16)
9 verbose stack at EventEmitter.emit (events.js:110:17)
9 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:14:12)
9 verbose stack at ChildProcess.emit (events.js:110:17)
9 verbose stack at maybeClose (child_process.js:1008:16)
9 verbose stack at Process.ChildProcess._handle.onexit (child_process.js:1080:5)
10 verbose pkgid [email protected]
11 verbose cwd /home/ubuntu/proj/globlee
12 error Linux 3.13.0-36-generic
13 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "start"
14 error node v0.12.0
15 error npm v2.5.1
16 error code ELIFECYCLE
17 error [email protected] start: node ./bin/www
17 error Exit status 139
18 error Failed at the [email protected] start script 'node ./bin/www'.
18 error This is most likely a problem with the globlee package,
18 error not with npm itself.
18 error Tell the author that this fails on your system:
18 error node ./bin/www
18 error You can get their info via:
18 error npm owner ls globlee
18 error There is likely additional logging output above.
19 verbose exit [ 1, true ]

How would this fare against bot-net

How well would express-brute fair against large scale bot-nets? Can it help mitigate mis-use of wide-open API's, or can the attacker simply keep switching IP's and filling up the mongo-db?

Would like to get some general defaults, if possible.

Deprecated use of res.send

Using Express 4, get:

express deprecated res.send(status, body): Use res.status(status).send(body) instead at node_modules/express-brute/index.js:163:6

global bruteforce count is not updating on more than 1000 concurrent requests

On using express brute package and applying a global limit, the count is not updated properly in express brute store. I’ve tried both mongodb and redis db as brute force stores but still the same results are observed. I tried doing the same using JMeter and made a load request of 1000 concurrent requests to my API. Once all the 1000 requests are completed, the count in the express brute store doesn’t gets increased to more than 150 ever. The same thing if I try with 100/200 concurrent requests, the count is updated correct in the redis db.
So can anyone confirm whether they also get similar results with such a use case ?

Express deprecated

When I use this module, I receive this warning. I think you should fix it.

express deprecated res.send(status, body): Use res.status(status).send(body) instead node_modules\express-brute\index.js:163:6

Make IP optional parameter for reset

Right now reset requires an ip, but with the ignoreIP flag you may want to do a reset without passing one. Thoughts on either making IP an optional argument or not passing it to '_getKey()' if it's undefined?

I'm happy to make the PR for whichever case is preferable.

ExpressBrute.prototype.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));
};

Not working?

Not the first time I'm implementing this so a little odd...

var expressBrute = require('express-brute');
var bruteProtection = new expressBrute(new expressBrute.MemoryStore(), {
    freeRetries: 2,
    minWait: 5*60*1000, // 5 minutes
    maxWait: 60&60&1000, // 1 hour
});
app.all('/login', bruteProtection.prevent, function(req, res, next){
    log.trace('calls login');
    next();
});

However, it just keeps returning 401 (because I made login fail) -- no 429. I can't get it to ring 429 even once.

I've put this code at the beginning of my express/app stack and still no-go. Above it loads views/view engine of jade, sets static paths for js/css serving, morgan, and express session.

I'm not sure how to debug this.

Switch to Mocha for tests

I want to switch to mocha for tests, it seems like a much more flexible setup, and in particular has some good support for mocking time (with sinon) that will simplify a lot of the tests.

options on getMiddleware

What is the reason to have a option parameter on both the constructor and getMiddleware?
In the example I see you are creating 2 objects (userBruteforce and globalBruteforce). Why you don't not centralize the configuration in one place (for example the constuctor)?

Ability to query status without incrementing

I have a server where clients authenticate themselves with tokens. I want to make sure, nobody tries sending random tokens to find a working one. Random is the keyword, as I want to allow multiple requests using the same valid token. I cannot just reset upon a valid token as the attacker could easily create a token for itself, try breaking in but at the last attempt before protection kicks in use its valid token.

My solution would be:
1: query if I can check that the token is valid
1.1: if I can't, just reject the request
1.2: if I can, check that the token is valid
1.2.1: if valid, allow in, do nothing with the brute
1.2.2: if isn't valid, increment the brute counter

tl;dr: I would like to method where I can just query the state and I would like to manually increment the request count.

Duplicate key error in collection when using mongo or mongoose stores

I was working with the express-brute-mongo store, and encountered this error:

Error: Cannot increment request count
errmsg: 'E11000 duplicate key error collection: mydb.bruteforce index: _id_ dup key: { : "G/IAnx1Agvj9X0JzMvChzHFl6jKU0ps4dnfVvJ+ZYTc=" }' } }

Since I couldn't get this resolved in the the mongo store module, I've tried the
exact same implementation with the express-brute-mongoose store, and encountered
the same error. Since the error gets triggered in express-brute, I'm hoping to find answers here on how to resolve this.

This is the middleware I set up (same as defaults, but logging the dup key error):

var rateLimitMiddleware = new ExpressBrute(bruteStore, {
  failCallback: function (req, res, next, nextValidRequestDate) {
    var err = new Error('Too many attempts.');
    err.status = 429;
    next(err);
  },
  handleStoreError: function (error) {
    console.log(error);
  }
});

My implementation with express-brute-mongo was:

var MongoStore = require('express-brute-mongo');
var MongoClient = require('mongodb').MongoClient;

var bruteStore = new MongoStore(function (ready) {
  MongoClient.connect(process.env['DB_URL'], function (err, db) {
    if (err) throw err;
    db.collection('ratelimits').ensureIndex({expires: 1}, {expireAfterSeconds: 0});
    ready(db.collection('ratelimits'));
  });
});

My implementation with express-brute-mongoose is:

var MongooseStore = require('express-brute-mongoose');
var BruteForceSchema = require('express-brute-mongoose/dist/schema');
var bruteModel = mongoose.model('bruteforce', BruteForceSchema);
var bruteStore = new MongooseStore(bruteModel);

Any help why i'm hitting dup key MongoErrors would be greatly appreciated.

Errors are not handled correctly

Using the recommended express-brute-mongo addition, the node server will crash in the event that node loses connection with the IP logging table. There doesn't appear to be a way to catch the errors.
For example; A user makes a call to the node js server causing it to crash in the event that the server loses connection to the IP address store.

Why not use memoryStore in production?

var store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this in production

The only two downsides I see are:

  1. Rebooting clears any limits.
  2. Node.js clusters and load balanced servers don't share state

Both of which seem like false negatives. If I can save a request to Redis and the complexity, that's a win for me.

Other drawbacks? Does it leak memory over time? Thanks!

Sporadic blocking and unblocking of the requests

Encountered a strange behavior with same IP being randomly blocked / unblocked.

From the logs:

June 1st, 2015 [2015-06-01] 21:12:56.337 server1 INFO/request1 var1 var2 var3  clientIP
21:13:13.099 server2 INFO/request2 var1 var2 var3  clientIP
... tailing ...
June 1st, 2015 [2015-06-01] 21:14:00.371 server1 WARNClient sent too many requests, throttling IP  clientIP this request is  /request2
21:14:15.672 server2 INFO/request3 var1 var2 gem var3  clientIP
June 1st, 2015 [2015-06-01] 21:14:16.556 server2 INFO/request1 var1 var2 var3  clientIP
21:14:23.108 server2 INFO/request4 [{var4}] var3  clientIP
... tailing ...
June 1st, 2015 [2015-06-01] 21:14:31.015 server2 INFO/request4 [{var4}] var3  clientIP
... tailing ...
June 1st, 2015 [2015-06-01] 21:14:42.430 server2 INFO/request4 [{var4}] var3  clientIP
... tailing ...
June 1st, 2015 [2015-06-01] 21:15:00.500 server1 WARNClient sent too many requests, throttling IP  clientIP this request is  /request2
June 1st, 2015 [2015-06-01] 21:15:16.089 server2 INFO/request1 var1 var2 var3  clientIP
... tailing ...
June 1st, 2015 [2015-06-01] 21:16:00.938 server1 WARNClient sent too many requests, throttling IP  clientIP this request is  /request2
21:16:16.508 server2 INFO/request1 var1 var2 var3  clientIP
21:17:00.213 server1 WARNClient sent too many requests, throttling IP  clientIP this request is  /request2
21:17:15.866 server2 INFO/request1 var1 var2 var3  clientIP
21:18:13.038 server2 INFO/request4 [{var5}] var3  clientIP

The bruteforce settings are:

var genericBruteforce = new ExpressBrute(store, {
    freeRetries: 99,
    lifetime: 30,
    minWait: 31,
    maxWait: 31,
    attachResetToRequest: false,
    refreshTimeoutOnRequest: false,
    failCallback: someFn,
    handleStoreError: errorFn
});

The used Redis is Azure Redis caching service.

I've also tried erasing all the brute force keys from Redis, however once express-brute just recreates them, it starts the random blocking again.

Any idea what could be causing such behavior?

failCallback not triggered

Hello,
thanks for the very useful module.
I'm trying to implement it in my application but it is not working.
I tried both with Mongoose and with ExpressBrute.MemoryStore.

var failCallback = function (req, res, next, nextValidRequestDate) {
console.log('err');
};
var ExpressBrute = require('express-brute');
 
var store = new ExpressBrute.MemoryStore(); // stores state locally, don't use this in production 
var bruteforce = new ExpressBrute(store, {
    freeRetries: 3,
    minWait: 60*1000, // 1 minutes 
    maxWait: 60*60*1000,
    failCallback: failCallback
});
 
app.post('/auth',
    bruteforce.prevent, // error 429 if we hit this route too often 
    function (req, res, next) {
        console.log('aaa');
    }
);

// Brute-Force Limiter configuration
/*
var bruteForcemodel = mongoose.model('BruteForce', bruteForceSchema,'BruteForce');
var bruteForceStore = new mongooseStore(bruteForcemodel);
var bruteForce = new expressBrute(bruteForceStore,  { freeRetries: 2,
                                                      minWait: 10*1000, // 10 seconds
                                                      maxWait: 60*60*1000, // 1 hour
                                                      failCallback: failCallback
                                                    });
                                                    */

/*
app.post('/login',
    bruteForce.prevent, // error 429 if we hit this route too often 
    function (req, res, next) {
      console.log('asffa');
      res.send('Success!');
    }
);*/

If I try to send a POST from localhost or from a different IP, the failCallback is not triggered even after 100 calls.

I'm quite a noob and probably I am doing something wrong, could you please give me a hint?

Thank you.

Failing tests

I noticed that Travis showed the current version as failing, so I decided to run the tests myself.

Every once in a while, a test will fail:

OliverSalzburg@OLIVER-PC /d/temp/express-brute (master)
$ npm test

> [email protected] test d:\temp\express-brute
> node node_modules/jasmine-node/lib/jasmine-node/cli.js --captureExceptions --matchall spec

...........F.......................

Failures:

  1) express brute behavior passes the correct next request time
   Message:
     TypeError: Cannot call method 'getTime' of undefined
   Stacktrace:
     TypeError: Cannot call method 'getTime' of undefined
    at null.<anonymous> (d:\temp\express-brute\spec\ExpessBrute.js:114:44)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

Finished in 3.242 seconds
35 tests, 81 assertions, 1 failure, 0 skipped


npm ERR! Test failed.  See above for more details.
npm ERR! not ok code 0

If I run the test suite again, everything might pass or I might get the same failure again.

The results seem kinda unreliable. What's the issue here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.