express-brute vs express-rate-limit vs express-slow-down vs rate-limiter-flexible
Node.js 速率限制库
express-bruteexpress-rate-limitexpress-slow-downrate-limiter-flexible类似的npm包:

Node.js 速率限制库

速率限制库用于控制客户端对服务器的请求频率,以防止滥用和保护服务器资源。这些库提供了不同的策略和功能来实现请求限制,确保应用程序的稳定性和安全性。通过使用这些库,开发者可以有效地管理流量,防止恶意攻击,如拒绝服务攻击(DoS),并为合法用户提供更好的体验。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
express-brute0568-219 年前BSD
express-rate-limit03,234141 kB1110 天前MIT
express-slow-down029937.6 kB18 天前MIT
rate-limiter-flexible03,500215 kB134 天前ISC

功能对比: express-brute vs express-rate-limit vs express-slow-down vs rate-limiter-flexible

存储后端支持

  • express-brute:

    支持多种存储后端,包括内存、Redis、MongoDB等,允许开发者根据需求选择合适的存储方式。

  • express-rate-limit:

    主要使用内存存储,适合小型应用,但不支持持久化存储,可能不适合大规模应用。

  • express-slow-down:

    与 express-rate-limit 相似,主要依赖内存存储,适合小型项目。

  • rate-limiter-flexible:

    支持多种存储后端,包括内存、Redis、MongoDB等,提供更高的灵活性和可扩展性。

功能复杂性

  • express-brute:

    提供简单的速率限制功能,适合快速实现基本需求,但功能相对较少。

  • express-rate-limit:

    功能简单,易于使用,适合大多数常见的速率限制需求,但缺乏复杂的自定义选项。

  • express-slow-down:

    在请求超过限制时减慢响应速度,提供更友好的用户体验,但功能相对单一。

  • rate-limiter-flexible:

    功能强大,支持复杂的限制策略和自定义选项,适合需要高度可配置的应用。

易用性

  • express-brute:

    易于集成和使用,适合快速开发和原型设计。

  • express-rate-limit:

    非常易于使用,提供简单的 API,适合快速集成到现有的 Express 应用中。

  • express-slow-down:

    简单易用,适合需要减慢响应速度的场景,集成方便。

  • rate-limiter-flexible:

    虽然功能强大,但配置相对复杂,适合有一定经验的开发者。

性能影响

  • express-brute:

    在高并发场景下,可能会对性能产生一定影响,尤其是使用内存存储时。

  • express-rate-limit:

    在请求量较大时,可能会导致性能下降,尤其是在内存存储模式下。

  • express-slow-down:

    通过减慢响应速度来处理请求,可能会影响用户体验,但可以有效保护服务器。

  • rate-limiter-flexible:

    性能表现良好,尤其是在使用 Redis 等外部存储时,能够处理高并发请求。

社区支持与维护

  • express-brute:

    社区支持相对较小,更新频率较低,适合简单项目。

  • express-rate-limit:

    拥有活跃的社区和良好的文档,更新频繁,适合大多数项目。

  • express-slow-down:

    社区支持一般,更新较少,适合特定需求的项目。

  • rate-limiter-flexible:

    社区活跃,文档完善,更新频繁,适合需要长期维护的项目。

如何选择: express-brute vs express-rate-limit vs express-slow-down vs rate-limiter-flexible

  • express-brute:

    选择 express-brute 如果你需要一个简单易用的速率限制解决方案,支持多种存储后端(如内存、Redis等),并且希望能够灵活地配置限制策略。它适合需要快速实现速率限制功能的项目。

  • express-rate-limit:

    选择 express-rate-limit 如果你需要一个轻量级的速率限制中间件,能够快速集成到现有的 Express 应用中。它提供了基本的速率限制功能,并且易于配置和使用,适合大多数常见的速率限制需求。

  • express-slow-down:

    选择 express-slow-down 如果你希望在请求超过限制时减慢响应速度,而不是完全拒绝请求。这种策略可以在保护服务器的同时,给用户提供一定的响应,适合需要平衡用户体验和安全性的场景。

  • rate-limiter-flexible:

    选择 rate-limiter-flexible 如果你需要一个功能强大且灵活的速率限制解决方案,支持多种存储后端(如内存、Redis、MongoDB等),并且需要复杂的限制策略和自定义功能。它适合需要高度可配置和可扩展性的应用。

express-brute的README

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');

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!');
	}
);

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 retires 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 or 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 protect.

Built-in Failure Callbacks

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

  • ExpressBrute.FailTooManyRquests 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 = 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
		}
	}
);

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.