express-rate-limit 5.5.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md DELETED
@@ -1,350 +0,0 @@
1
- # Express Rate Limit
2
-
3
- [![Node.js CI](https://github.com/nfriedly/express-rate-limit/workflows/Node.js%20CI/badge.svg)](https://github.com/nfriedly/express-rate-limit/actions)
4
- [![NPM version](https://img.shields.io/npm/v/express-rate-limit.svg)](https://npmjs.org/package/express-rate-limit "View this project on NPM")
5
- [![npm downloads](https://img.shields.io/npm/dm/express-rate-limit)](https://www.npmjs.com/package/express-rate-limit)
6
-
7
-
8
- Basic rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.
9
-
10
- Plays nice with [express-slow-down](https://www.npmjs.com/package/express-slow-down).
11
-
12
- Note: this module does not share state with other processes/servers by default. It also buckets all requests to an internal clock rather than starting a new timer for each end-user. It's fine for abuse-prevention but might not produce the desired effect when attempting to strictly enforce API rate-limits or similar. If you need a more robust solution, I recommend using an external store:
13
-
14
- ### Stores
15
-
16
- - Memory Store _(default, built-in)_ - stores hits in-memory in the Node.js process. Does not share state with other servers or processes, and does not start a separate timer for each end user.
17
- - [Redis Store](https://npmjs.com/package/rate-limit-redis)
18
- - [Memcached Store](https://npmjs.org/package/rate-limit-memcached)
19
- - [Mongo Store](https://www.npmjs.com/package/rate-limit-mongo)
20
- - [Precise Memory Store](https://www.npmjs.com/package/precise-memory-rate-limit) - similar to the built-in memory store except that it stores a distinct timestamp for each IP rather than bucketing them together.
21
-
22
- ### Alternate Rate-limiters
23
-
24
- This module was designed to only handle the basics and didn't even support external stores initially. These other options all are excellent pieces of software and may be more appropriate for some situations:
25
-
26
- - [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
27
- - [express-brute](https://www.npmjs.com/package/express-brute)
28
- - [rate-limiter](https://www.npmjs.com/package/express-limiter)
29
-
30
- ## Install
31
-
32
- ```sh
33
- $ npm install --save express-rate-limit
34
- ```
35
-
36
- ## Usage
37
-
38
- For an API-only server where the rate-limiter should be applied to all requests:
39
-
40
- ```js
41
- const rateLimit = require("express-rate-limit");
42
-
43
- // Enable if you're behind a reverse proxy (Heroku, Bluemix, AWS ELB or API Gateway, Nginx, etc)
44
- // see https://expressjs.com/en/guide/behind-proxies.html
45
- // app.set('trust proxy', 1);
46
-
47
- const limiter = rateLimit({
48
- windowMs: 15 * 60 * 1000, // 15 minutes
49
- max: 100 // limit each IP to 100 requests per windowMs
50
- });
51
-
52
- // apply to all requests
53
- app.use(limiter);
54
- ```
55
-
56
- For a "regular" web server (e.g. anything that uses `express.static()`), where the rate-limiter should only apply to certain requests:
57
-
58
- ```js
59
- const rateLimit = require("express-rate-limit");
60
-
61
- // Enable if you're behind a reverse proxy (Heroku, Bluemix, AWS ELB or API Gateway, Nginx, etc)
62
- // see https://expressjs.com/en/guide/behind-proxies.html
63
- // app.set('trust proxy', 1);
64
-
65
- const apiLimiter = rateLimit({
66
- windowMs: 15 * 60 * 1000, // 15 minutes
67
- max: 100
68
- });
69
-
70
- // only apply to requests that begin with /api/
71
- app.use("/api/", apiLimiter);
72
- ```
73
-
74
- Create multiple instances to apply different rules to different routes:
75
-
76
- ```js
77
- const rateLimit = require("express-rate-limit");
78
-
79
- // Enable if you're behind a reverse proxy (Heroku, Bluemix, AWS ELB, Nginx, etc)
80
- // see https://expressjs.com/en/guide/behind-proxies.html
81
- // app.set('trust proxy', 1);
82
-
83
- const apiLimiter = rateLimit({
84
- windowMs: 15 * 60 * 1000, // 15 minutes
85
- max: 100
86
- });
87
- app.use("/api/", apiLimiter);
88
-
89
- const createAccountLimiter = rateLimit({
90
- windowMs: 60 * 60 * 1000, // 1 hour window
91
- max: 5, // start blocking after 5 requests
92
- message:
93
- "Too many accounts created from this IP, please try again after an hour"
94
- });
95
- app.post("/create-account", createAccountLimiter, function(req, res) {
96
- //...
97
- });
98
- ```
99
-
100
- **Note:** most stores will require additional configuration, such as custom prefixes, when using multiple instances. The default built-in memory store is an exception to this rule.
101
-
102
- ## Request API
103
-
104
- A `req.rateLimit` property is added to all requests with the `limit`, `current`, and `remaining` number of requests and, if the store provides it, a `resetTime` Date object. These may be used in your application code to take additional actions or inform the user of their status.
105
-
106
- The property name can be configured with the configuration option `requestPropertyName`
107
-
108
- ## Configuration options
109
-
110
- ### max
111
-
112
- Max number of connections during `windowMs` milliseconds before sending a 429 response.
113
-
114
- May be a number, or a function that returns a number or a promise. If `max` is a function, it will be called with `req` and `res` params.
115
-
116
- Defaults to `5`. Set to `0` to disable.
117
-
118
- Example of using a function:
119
-
120
- ```js
121
- const rateLimit = require("express-rate-limit");
122
-
123
- function isPremium(req) {
124
- //...
125
- }
126
-
127
- const limiter = rateLimit({
128
- windowMs: 15 * 60 * 1000, // 15 minutes
129
-
130
- // max could also be an async function or return a promise
131
- max: function(req, res) {
132
- if (isPremium(req)) {
133
- return 10;
134
- }
135
- return 5;
136
- }
137
- });
138
-
139
- // apply to all requests
140
- app.use(limiter);
141
- ```
142
-
143
- ### windowMs
144
-
145
- Timeframe for which requests are checked/remembered. Also used in the Retry-After header when the limit is reached.
146
-
147
- Note: with non-default stores, you may need to configure this value twice, once here and once on the store. In some cases the units also differ (e.g. seconds vs miliseconds)
148
-
149
- Defaults to `60000` (1 minute).
150
-
151
- ### message
152
-
153
- Error message sent to user when `max` is exceeded.
154
-
155
- May be a String, JSON object, or any other value that Express's [res.send](https://expressjs.com/en/4x/api.html#res.send) supports.
156
-
157
- Defaults to `'Too many requests, please try again later.'`
158
-
159
- ### statusCode
160
-
161
- HTTP status code returned when `max` is exceeded.
162
-
163
- Defaults to `429`.
164
-
165
- ### headers
166
-
167
- Enable headers for request limit (`X-RateLimit-Limit`) and current usage (`X-RateLimit-Remaining`) on all responses and time to wait before retrying (`Retry-After`) when `max` is exceeded.
168
-
169
- Defaults to `true`. Behavior may change in the next major release.
170
-
171
- ### draft_polli_ratelimit_headers
172
-
173
- Enable headers conforming to the [ratelimit standardization proposal](https://tools.ietf.org/id/draft-polli-ratelimit-headers-01.html): `RateLimit-Limit`, `RateLimit-Remaining`, and, if the store supports it, `RateLimit-Reset`. May be used in conjunction with, or instead of the `headers` option.
174
-
175
- Defaults to `false`. Behavior and name will likely change in future releases.
176
-
177
- ### keyGenerator
178
-
179
- Function used to generate keys.
180
-
181
- Defaults to req.ip, similar to this:
182
-
183
- ```js
184
- function (req /*, res*/) {
185
- return req.ip;
186
- }
187
- ```
188
-
189
- ### handler
190
-
191
- The function to handle requests once the max limit is exceeded. It receives the request and the response objects. The "next" param is available if you need to pass to the next middleware. Finally, the options param has all of the options that originally passed in when creating the current limiter and the default values for other options.
192
-
193
- The`req.rateLimit` object has `limit`, `current`, and `remaining` number of requests and, if the store provides it, a `resetTime` Date object.
194
-
195
- Defaults to:
196
-
197
- ```js
198
- function (req, res, next, options) {
199
- res.status(options.statusCode).send(options.message);
200
- }
201
- ```
202
-
203
- ### onLimitReached
204
-
205
- Function that is called the first time a user hits the rate limit within a given window.
206
-
207
- The`req.rateLimit` object has `limit`, `current`, and `remaining` number of requests and, if the store provides it, a `resetTime` Date object.
208
-
209
- Default is an empty function:
210
-
211
- ```js
212
- function (req, res, options) {
213
- /* empty */
214
- }
215
- ```
216
-
217
- ### requestWasSuccessful
218
-
219
- Function that is called when `skipFailedRequests` and/or `skipSuccessfulRequests` are set to `true`.
220
- May be overridden if, for example, a service sends out a 200 status code on errors.
221
-
222
- Defaults to
223
-
224
- ```js
225
- function (req, res) {
226
- return res.statusCode < 400;
227
- }
228
- ```
229
-
230
- ### skipFailedRequests
231
-
232
- When set to `true`, failed requests won't be counted. Request considered failed when:
233
-
234
- - response status >= 400
235
- - requests that were cancelled before last chunk of data was sent (response `close` event triggered)
236
- - response `error` event was triggrered by response
237
-
238
- (Technically they are counted and then un-counted, so a large number of slow requests all at once could still trigger a rate-limit. This may be fixed in a future release.)
239
-
240
- Defaults to `false`.
241
-
242
- ### skipSuccessfulRequests
243
-
244
- When set to `true` successful requests (response status < 400) won't be counted.
245
- (Technically they are counted and then un-counted, so a large number of slow requests all at once could still trigger a rate-limit. This may be fixed in a future release.)
246
-
247
- Defaults to `false`.
248
-
249
- ### skip
250
-
251
- Function used to skip (whitelist) requests. Returning `true`, or a promise that resolves with `true`, from the function will skip limiting for that request.
252
-
253
- Defaults to always `false` (count all requests):
254
-
255
- ```js
256
- function (/*req, res*/) {
257
- return false;
258
- }
259
- ```
260
-
261
- ### requestPropertyName
262
- Parameter to add to `req`-Object.
263
-
264
- Defaults to `rateLimit`.
265
-
266
- ### store
267
-
268
- The storage to use when persisting rate limit attempts.
269
-
270
- By default, the [MemoryStore](lib/memory-store.js) is used.
271
-
272
- Available data stores are:
273
-
274
- - MemoryStore: _(default)_ Simple in-memory option. Does not share state when app has multiple processes or servers.
275
- - [rate-limit-redis](https://npmjs.com/package/rate-limit-redis): A [Redis](http://redis.io/)-backed store, more suitable for large or demanding deployments.
276
- - [rate-limit-memcached](https://npmjs.org/package/rate-limit-memcached): A [Memcached](https://memcached.org/)-backed store.
277
- - [rate-limit-mongo](https://www.npmjs.com/package/rate-limit-mongo): A [MongoDB](https://www.mongodb.com/)-backed store.
278
-
279
- You may also create your own store. It must implement the following in order to function:
280
-
281
- ```js
282
- function SomeStore() {
283
- /**
284
- * Increments the value in the underlying store for the given key.
285
- * @method function
286
- * @param {string} key - The key to use as the unique identifier passed
287
- * down from RateLimit.
288
- * @param {Function} cb - The callback issued when the underlying
289
- * store is finished.
290
- *
291
- * The callback should be called with three values:
292
- * - error (usually null)
293
- * - hitCount for this IP
294
- * - resetTime - JS Date object (optional, but necessary for X-RateLimit-Reset header)
295
- */
296
- this.incr = function(key, cb) {
297
- // increment storage
298
- cb(null, hits, resetTime);
299
- };
300
-
301
- /**
302
- * Decrements the value in the underlying store for the given key. Used only when skipFailedRequests is true
303
- * @method function
304
- * @param {string} key - The key to use as the unique identifier passed
305
- * down from RateLimit.
306
- */
307
- this.decrement = function(key) {
308
- // decrement storage
309
- };
310
-
311
- /**
312
- * Resets a value with the given key.
313
- * @method function
314
- * @param {[type]} key - The key to reset
315
- */
316
- this.resetKey = function(key) {
317
- // remove key from storage or reset it to 0
318
- };
319
- }
320
- ```
321
-
322
- ## Instance API
323
-
324
- ### instance.resetKey(key)
325
-
326
- Resets the rate limiting for a given key. (Allow users to complete a captcha or whatever to reset their rate limit, then call this method.)
327
-
328
- ## Summary of breaking changes:
329
-
330
- ### v5 changes
331
-
332
- - Removed index.d.ts. (See [#138](https://github.com/nfriedly/express-rate-limit/issues/138))
333
-
334
- ### v4 Changes
335
-
336
- - Express Rate Limit no longer modifies the passed-in options object, it instead makes a clone of it.
337
-
338
- ### v3 Changes
339
-
340
- - Removed `delayAfter` and `delayMs` options; they were moved to a new module: [express-slow-down](https://npmjs.org/package/express-slow-down).
341
- - Simplified the default `handler` function so that it no longer changes the response format. Now uses [res.send](https://expressjs.com/en/4x/api.html#res.send).
342
- - `onLimitReached` now only triggers once for a given ip and window. only `handle` is called for every blocked request.
343
-
344
- ### v2 Changes
345
-
346
- v2 uses a less precise but less resource intensive method of tracking hits from a given IP. v2 also adds the `limiter.resetKey()` API and removes the `global: true` option.
347
-
348
- ## License
349
-
350
- MIT © [Nathan Friedly](http://nfriedly.com/)
@@ -1,190 +0,0 @@
1
- "use strict";
2
- const MemoryStore = require("./memory-store");
3
-
4
- function RateLimit(options) {
5
- options = Object.assign(
6
- {
7
- windowMs: 60 * 1000, // milliseconds - how long to keep records of requests in memory
8
- max: 5, // max number of recent connections during `window` milliseconds before sending a 429 response
9
- message: "Too many requests, please try again later.",
10
- statusCode: 429, // 429 status = Too Many Requests (RFC 6585)
11
- headers: true, //Send custom rate limit header with limit and remaining
12
- draft_polli_ratelimit_headers: false, //Support for the new RateLimit standardization headers
13
- // ability to manually decide if request was successful. Used when `skipSuccessfulRequests` and/or `skipFailedRequests` are set to `true`
14
- requestWasSuccessful: function (req, res) {
15
- return res.statusCode < 400;
16
- },
17
- skipFailedRequests: false, // Do not count failed requests
18
- skipSuccessfulRequests: false, // Do not count successful requests
19
- // allows to create custom keys (by default user IP is used)
20
- keyGenerator: function (req /*, res*/) {
21
- if (!req.ip) {
22
- console.error(
23
- "express-rate-limit: req.ip is undefined - you can avoid this by providing a custom keyGenerator function, but it may be indicative of a larger issue."
24
- );
25
- }
26
- return req.ip;
27
- },
28
- skip: function (/*req, res*/) {
29
- return false;
30
- },
31
- handler: function (req, res /*, next, optionsUsed*/) {
32
- res.status(options.statusCode).send(options.message);
33
- },
34
- onLimitReached: function (/*req, res, optionsUsed*/) {},
35
- requestPropertyName: "rateLimit", // Parameter name appended to req object
36
- },
37
- options
38
- );
39
-
40
- // store to use for persisting rate limit data
41
- options.store = options.store || new MemoryStore(options.windowMs);
42
-
43
- // ensure that the store has the incr method
44
- if (
45
- typeof options.store.incr !== "function" ||
46
- typeof options.store.resetKey !== "function" ||
47
- (options.skipFailedRequests &&
48
- typeof options.store.decrement !== "function")
49
- ) {
50
- throw new Error("The store is not valid.");
51
- }
52
-
53
- ["global", "delayMs", "delayAfter"].forEach((key) => {
54
- // note: this doesn't trigger if delayMs or delayAfter are set to 0, because that essentially disables them
55
- if (options[key]) {
56
- throw new Error(
57
- `The ${key} option was removed from express-rate-limit v3.`
58
- );
59
- }
60
- });
61
-
62
- function rateLimit(req, res, next) {
63
- Promise.resolve(options.skip(req, res))
64
- .then((skip) => {
65
- if (skip) {
66
- return next();
67
- }
68
-
69
- const key = options.keyGenerator(req, res);
70
-
71
- options.store.incr(key, function (err, current, resetTime) {
72
- if (err) {
73
- return next(err);
74
- }
75
-
76
- const maxResult =
77
- typeof options.max === "function"
78
- ? options.max(req, res)
79
- : options.max;
80
-
81
- Promise.resolve(maxResult)
82
- .then((max) => {
83
- req[options.requestPropertyName] = {
84
- limit: max,
85
- current: current,
86
- remaining: Math.max(max - current, 0),
87
- resetTime: resetTime,
88
- };
89
-
90
- if (options.headers && !res.headersSent) {
91
- res.setHeader("X-RateLimit-Limit", max);
92
- res.setHeader(
93
- "X-RateLimit-Remaining",
94
- req[options.requestPropertyName].remaining
95
- );
96
- if (resetTime instanceof Date) {
97
- // if we have a resetTime, also provide the current date to help avoid issues with incorrect clocks
98
- res.setHeader("Date", new Date().toUTCString());
99
- res.setHeader(
100
- "X-RateLimit-Reset",
101
- Math.ceil(resetTime.getTime() / 1000)
102
- );
103
- }
104
- }
105
- if (options.draft_polli_ratelimit_headers && !res.headersSent) {
106
- res.setHeader("RateLimit-Limit", max);
107
- res.setHeader(
108
- "RateLimit-Remaining",
109
- req[options.requestPropertyName].remaining
110
- );
111
- if (resetTime) {
112
- const deltaSeconds = Math.ceil(
113
- (resetTime.getTime() - Date.now()) / 1000
114
- );
115
- res.setHeader("RateLimit-Reset", Math.max(0, deltaSeconds));
116
- }
117
- }
118
-
119
- if (
120
- options.skipFailedRequests ||
121
- options.skipSuccessfulRequests
122
- ) {
123
- let decremented = false;
124
- const decrementKey = () => {
125
- if (!decremented) {
126
- options.store.decrement(key);
127
- decremented = true;
128
- }
129
- };
130
-
131
- if (options.skipFailedRequests) {
132
- res.on("finish", function () {
133
- if (!options.requestWasSuccessful(req, res)) {
134
- decrementKey();
135
- }
136
- });
137
-
138
- res.on("close", () => {
139
- if (!res.finished) {
140
- decrementKey();
141
- }
142
- });
143
-
144
- res.on("error", () => decrementKey());
145
- }
146
-
147
- if (options.skipSuccessfulRequests) {
148
- res.on("finish", function () {
149
- if (options.requestWasSuccessful(req, res)) {
150
- options.store.decrement(key);
151
- }
152
- });
153
- }
154
- }
155
-
156
- if (max && current === max + 1) {
157
- options.onLimitReached(req, res, options);
158
- }
159
-
160
- if (max && current > max) {
161
- if (options.headers && !res.headersSent) {
162
- res.setHeader(
163
- "Retry-After",
164
- Math.ceil(options.windowMs / 1000)
165
- );
166
- }
167
- return options.handler(req, res, next, options);
168
- }
169
-
170
- next();
171
-
172
- return null;
173
- })
174
- .catch(next);
175
- });
176
-
177
- return null;
178
- })
179
- .catch(next);
180
- }
181
-
182
- rateLimit.resetKey = options.store.resetKey.bind(options.store);
183
-
184
- // Backward compatibility function
185
- rateLimit.resetIp = rateLimit.resetKey;
186
-
187
- return rateLimit;
188
- }
189
-
190
- module.exports = RateLimit;
@@ -1,47 +0,0 @@
1
- "use strict";
2
-
3
- function calculateNextResetTime(windowMs) {
4
- const d = new Date();
5
- d.setMilliseconds(d.getMilliseconds() + windowMs);
6
- return d;
7
- }
8
-
9
- function MemoryStore(windowMs) {
10
- let hits = {};
11
- let resetTime = calculateNextResetTime(windowMs);
12
-
13
- this.incr = function (key, cb) {
14
- if (hits[key]) {
15
- hits[key]++;
16
- } else {
17
- hits[key] = 1;
18
- }
19
-
20
- cb(null, hits[key], resetTime);
21
- };
22
-
23
- this.decrement = function (key) {
24
- if (hits[key]) {
25
- hits[key]--;
26
- }
27
- };
28
-
29
- // export an API to allow hits all IPs to be reset
30
- this.resetAll = function () {
31
- hits = {};
32
- resetTime = calculateNextResetTime(windowMs);
33
- };
34
-
35
- // export an API to allow hits from one IP to be reset
36
- this.resetKey = function (key) {
37
- delete hits[key];
38
- };
39
-
40
- // simply reset ALL hits every windowMs
41
- const interval = setInterval(this.resetAll, windowMs);
42
- if (interval.unref) {
43
- interval.unref();
44
- }
45
- }
46
-
47
- module.exports = MemoryStore;