mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Updated comments and line sizes in markdown and rate limiter package
This commit is contained in:
@@ -401,8 +401,14 @@ client, no arguments are passed.
|
||||
|
||||
<h3 id="accounts_rate_limit"><span>Rate Limiting</span></h3>
|
||||
|
||||
By default, there are rules added to the [`DDPRateLimiter`](#DDPRateLimiter) that rate limit logins, new user registration and password reset calls to a limit of 5 requests per 10 seconds per IP address. These are a basic solution to dictionary attacks where a malicious user attempts to guess the passwords of legitimate users by attempting all possible passwords.
|
||||
By default, there are rules added to the [`DDPRateLimiter`](#DDPRateLimiter)
|
||||
that rate limit logins, new user registration and password reset calls to a
|
||||
limit of 5 requests per 10 seconds per IP address. These are a basic solution
|
||||
to dictionary attacks where a malicious user attempts to guess the passwords
|
||||
of legitimate users by attempting all possible passwords.
|
||||
|
||||
These rate limiting rules can be removed by calling `Accounts.removeDefaultRateLimit()`. Please see the [`DDPRateLimiter`](#DDPRateLimiter) docs for more information.
|
||||
These rate limiting rules can be removed by calling
|
||||
`Accounts.removeDefaultRateLimit()`. Please see the
|
||||
[`DDPRateLimiter`](#DDPRateLimiter) docs for more information.
|
||||
|
||||
{{/template}}
|
||||
|
||||
@@ -7,15 +7,16 @@ Ap._defaultRateLimiterRuleId = DDPRateLimiter.addRule({
|
||||
userId: null,
|
||||
ipAddr: null,
|
||||
type: 'method',
|
||||
name: function(name) {
|
||||
return _.contains(['login', 'createUser', 'resetPassword', 'forgotPassword'], name);
|
||||
name: function (name) {
|
||||
return _.contains(['login', 'createUser', 'resetPassword',
|
||||
'forgotPassword'], name);
|
||||
},
|
||||
sessionId: function(sessionId) {
|
||||
return true;
|
||||
sessionId: function (sessionId) {
|
||||
return true;
|
||||
}
|
||||
}, 5, 10000);
|
||||
|
||||
// Removes default rate limiting rule
|
||||
Ap.removeDefaultRateLimit = function () {
|
||||
return DDPRateLimiter.removeRule(Ap._defaultRateLimiterRuleId);
|
||||
return DDPRateLimiter.removeRule(Ap._defaultRateLimiterRuleId);
|
||||
}
|
||||
@@ -3,82 +3,11 @@
|
||||
// * RateLimiter - a general rate limiter that stores rules and determines
|
||||
// whether inputs are allowed
|
||||
//
|
||||
// ** Rules **
|
||||
// Rules are composed of the following fields:
|
||||
// - id Random id generated and used to key the rule in the rate limiter
|
||||
// - options Object that contains the intervalTime after which the rule is reset, and the
|
||||
// number of calls that are allowed in the specified interval time
|
||||
// - matchers Dictionary of keys that are searched for in the input provided to match
|
||||
// with values that define the set of values that match this rule. Values can be
|
||||
// objects or they can be functions that return a boolean of whether the provided
|
||||
// input matches. For example, if we only want to match all even ids, plus any other fields
|
||||
// , we could have a rule that included a key value pair as follows:
|
||||
// {
|
||||
// ...
|
||||
// id: function (id) {
|
||||
// return id % 2 === 0;
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
// A rule is only said to apply to a given input if every key in the matcher
|
||||
// matches to the input values.
|
||||
// - _lastResetTime Last time this rule's counters were reset. Last reset time is used to
|
||||
// keep track if the interval time has passed
|
||||
// - counters Dictionary that stores the current state of inputs and number times they've
|
||||
// been passed to the rate limiter. Unique keys are made per input per rule that
|
||||
// create a concatenated string of all keys in the rule with the values from the
|
||||
// input
|
||||
//
|
||||
// For example, if we had a rule with matchers as such:
|
||||
// {
|
||||
// userId: function(userId) {
|
||||
// return true;
|
||||
// },
|
||||
// methodName: 'hello'
|
||||
// }
|
||||
// and we were passed an input as follows:
|
||||
// {
|
||||
// userId: 'meteor'
|
||||
// methodName: 'hello'
|
||||
// }
|
||||
//
|
||||
// The key generated would be 'userIdmeteormethodNamehello'.
|
||||
//
|
||||
// These counters are checked on every invocation to determine whether a rate limit
|
||||
// has been reached.
|
||||
//
|
||||
// The methods provided are as follows:
|
||||
// * match(input) : Checks whether the rule applies to the provided input by comparing every
|
||||
// field in the matcher to the provided input. Order of input doesn't matter,
|
||||
// they just must contain the appropriate keys and the values must be allowed by
|
||||
// the matcher
|
||||
// * _generateKeyString(input) : generates a key string by concatenating all the keys in the matcher
|
||||
// with the corresponding values in the input
|
||||
// * apply(input) : applies the provided input and returns the key string, the time since last
|
||||
// reset and time to next reset
|
||||
// * resetCounter() : resets the counters for this rule and sets _lastResetTime to be the current time
|
||||
//
|
||||
// ** Rate Limiter **
|
||||
// A rate limiter stores a dictionary of rules keyed by their rule Id. It provides the following methods:
|
||||
//
|
||||
// * addRule(rule, numRequests, intervalTime) : adds a Rule to dictionary of rules and returns the user
|
||||
// the rule Id
|
||||
// * removeRule(id) : removes a rule from the dictionary and returns boolean detailing success
|
||||
// * check(input) : checks all rules that apply to this input to see if any rate limits have been exceeded
|
||||
// Returns an object as follows:
|
||||
// {
|
||||
// allowed: boolean - is this input allowed
|
||||
// timeToReset: integer - returns time to reset in milliseconds
|
||||
// numInvocationsLeft: integer - returns number of calls left before limit is reached
|
||||
// }
|
||||
// If multiple rules match, the least number of invocations left is returned. If the rate limit
|
||||
// has been reached, the longest timeToReset is returned.
|
||||
//
|
||||
// * increment(input) : increments counters in all rules that apply to this input.
|
||||
// * _findAllMatchingRules(input) : returns an array of all rules that apply to provided input
|
||||
//
|
||||
|
||||
|
||||
// Rate limiters analyze a series of "inputs" (which are POJOs) by running
|
||||
// them against a set of "rules". Rules specify which inputs they match by
|
||||
// running configurable "matcher" functions on keys in the event object). A
|
||||
// `check` method returns whether this input should be allowed, the time
|
||||
// until next reset and the number of calls for this input left.
|
||||
|
||||
// Default time interval (in milliseconds) to reset rate limit counters
|
||||
var DEFAULT_INTERVAL_TIME_IN_MILLISECONDS = 1000;
|
||||
@@ -107,7 +36,9 @@ var Rule = function (options, matchers) {
|
||||
_.extend(Rule.prototype, {
|
||||
// Determine if this rule applies to the given input by comparing all
|
||||
// rule.matchers. If the match fails, search short circuits instead of
|
||||
// iterating through all matchers.
|
||||
// iterating through all matchers. The order of the input doesn't matter,
|
||||
// it just must contain the appropriate keys and their respective values
|
||||
// must be allowed by the matcher.
|
||||
match: function (input) {
|
||||
var self = this;
|
||||
var ruleMatches = true;
|
||||
@@ -134,7 +65,8 @@ _.extend(Rule.prototype, {
|
||||
return ruleMatches;
|
||||
},
|
||||
|
||||
// Generates unique key string for provided input
|
||||
// Generates unique key string for provided input by concatenating all the
|
||||
// keys in the matcher with the corresponding values in the input.
|
||||
// Only called if rule matches input.
|
||||
_generateKeyString: function (input) {
|
||||
var self = this;
|
||||
@@ -153,8 +85,8 @@ _.extend(Rule.prototype, {
|
||||
return returnString;
|
||||
},
|
||||
|
||||
// Generates the key, timeSinceLastReset and timeToNextReset once the rule
|
||||
// is applied
|
||||
// Applies the provided input and returns the key string, time since last
|
||||
// reset and time to next reset.
|
||||
apply: function (input) {
|
||||
var self = this;
|
||||
var keyString = self._generateKeyString(input);
|
||||
@@ -166,8 +98,9 @@ _.extend(Rule.prototype, {
|
||||
timeToNextReset: timeToNextReset
|
||||
};
|
||||
},
|
||||
// Reset all keys for this specific rule. Called once the timeSinceLastReset
|
||||
// has exceeded the intervalTime.
|
||||
// Reset counter dictionary for this specific rule. Called once the
|
||||
// timeSinceLastReset has exceeded the intervalTime. _lastResetTime is
|
||||
// set to be the current time in milliseconds.
|
||||
resetCounter: function () {
|
||||
var self = this;
|
||||
|
||||
@@ -189,9 +122,16 @@ RateLimiter = function () {
|
||||
|
||||
/**
|
||||
* Checks if this input has exceeded any rate limits.
|
||||
* @param {object} input dictionary containing key-value pairs of attributes that match to rules
|
||||
* @return {object} Returns object of whether input is allowed, time
|
||||
* to next reset and number invocations left
|
||||
* @param {object} input dictionary containing key-value pairs of attributes
|
||||
* that match to rules
|
||||
* @return {object} Returns object of following structure
|
||||
* { 'allowed': boolean - is this input allowed
|
||||
* 'timeToReset': integer - returns time to reset in milliseconds
|
||||
* 'numInvocationsLeft': integer - returns number of calls left before limit
|
||||
* is reached
|
||||
* }
|
||||
* If multiple rules match, the least number of invocations left is returned.
|
||||
* If the rate limit has been reached, the longest timeToReset is returned.
|
||||
*/
|
||||
RateLimiter.prototype.check = function (input) {
|
||||
var self = this;
|
||||
@@ -225,10 +165,10 @@ RateLimiter.prototype.check = function (input) {
|
||||
reply.allowed = false;
|
||||
reply.numInvocationsLeft = 0;
|
||||
} else {
|
||||
// If this is an allowed attempt and we haven't failed on any of the other rules that
|
||||
// match, update the reply field.
|
||||
if (rule.options.numRequestsAllowed - numInvocations < reply.numInvocationsLeft &&
|
||||
reply.allowed) {
|
||||
// If this is an allowed attempt and we haven't failed on any of the
|
||||
// other rules that match, update the reply field.
|
||||
if (rule.options.numRequestsAllowed - numInvocations <
|
||||
reply.numInvocationsLeft && reply.allowed) {
|
||||
reply.allowed = true;
|
||||
reply.timeToReset = ruleResult.timeToNextReset < 0 ?
|
||||
rule.options.intervalTime :
|
||||
@@ -241,17 +181,58 @@ RateLimiter.prototype.check = function (input) {
|
||||
return reply;
|
||||
}
|
||||
|
||||
// Each rule is composed of an `id`, an options object that contains the
|
||||
// `intervalTime` after which the rule is reset, and `numRequestsAllowed` in
|
||||
// the specified interval time, a dictionary of `matchers` whose keys are
|
||||
// searched for in the input provided to determine if there is a match. If the
|
||||
// values match, then the rules counters are incremented. Values can be objects
|
||||
// or they can be functions that return a boolean of whether the provided input
|
||||
// matches. For example, if we only want to match all even ids, plusany other
|
||||
// fields, we could have a rule that included a key-value pair as follows:
|
||||
// {
|
||||
// ...
|
||||
// id: function (id) {
|
||||
// return id % 2 === 0;
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
// A rule is only said to apply to a given input if every key in the matcher
|
||||
// matchesto the input values. There is also a dictionary of `counters` that
|
||||
// store the current state of inputs and number of times they've been passed
|
||||
// to the rate limiter. Unique keys are made per input per rule that create
|
||||
// a concatenated string of all keys in the rule with the values from the
|
||||
// input. For example, if we had a rule with matchers as such:
|
||||
// {
|
||||
// userId: function(userId) {
|
||||
// return true;
|
||||
// },
|
||||
// methodName: 'hello'
|
||||
// }
|
||||
// and we were passed an input as follows:
|
||||
// {
|
||||
// userId: 'meteor'
|
||||
// methodName: 'hello'
|
||||
// }
|
||||
// The key generated would be 'userIdmeteormethodNamehello'.
|
||||
// These counters are checked on every invocation to determine whether a rate
|
||||
// limit has been reached.
|
||||
|
||||
/**
|
||||
* Adds a rule to dictionary of rules that are checked against on every call. Only inputs that
|
||||
* pass all of the rules will be allowed and order doesn't matter.
|
||||
* @param {object} rule Input dictionary defining certain attributes and rules associated with them.
|
||||
* Each attribute's value can either be a value, a function or null. All functions must return a boolean
|
||||
* of whether the input is matched by that attribute's rule or not
|
||||
* Adds a rule to dictionary of rules that are checked against on every call.
|
||||
* Only inputs that pass all of the rules will be allowed and order doesn't
|
||||
* matter. Returns unique rule id that can be passed to 'removeRule'.
|
||||
* @param {object} rule Input dictionary defining certain attributes and
|
||||
* rules associated with them.
|
||||
* Each attribute's value can either be a value, a function or null. All
|
||||
* functions must return a boolean of whether the input is matched by that
|
||||
* attribute's rule or not
|
||||
* @param {integer} numRequestsAllowed Number of requests allowed per interval
|
||||
* @param {integer} intervalTime Number of milliseconds before interval is reset
|
||||
* @return {string} Returns the randomly generated rule id
|
||||
* @param {integer} intervalTime Number of milliseconds before interval
|
||||
* is reset
|
||||
* @return {string} Returns unique rule id
|
||||
*/
|
||||
RateLimiter.prototype.addRule = function (rule, numRequestsAllowed, intervalTime) {
|
||||
RateLimiter.prototype.addRule = function (rule, numRequestsAllowed,
|
||||
intervalTime) {
|
||||
var self = this;
|
||||
|
||||
var options = {
|
||||
@@ -266,8 +247,8 @@ RateLimiter.prototype.addRule = function (rule, numRequestsAllowed, intervalTime
|
||||
|
||||
/**
|
||||
* Increment counters in every rule that match to this input
|
||||
* @param {object} input Dictionary object containing attributes that may match to
|
||||
* rules
|
||||
* @param {object} input Dictionary object containing attributes that may
|
||||
* match to rules
|
||||
*/
|
||||
RateLimiter.prototype.increment = function (input) {
|
||||
var self = this;
|
||||
@@ -291,6 +272,7 @@ RateLimiter.prototype.increment = function (input) {
|
||||
});
|
||||
}
|
||||
|
||||
// Returns an array of all rules that apply to provided input
|
||||
RateLimiter.prototype._findAllMatchingRules = function (input) {
|
||||
var self = this;
|
||||
|
||||
@@ -302,7 +284,8 @@ RateLimiter.prototype._findAllMatchingRules = function (input) {
|
||||
return matchingRules;
|
||||
}
|
||||
/**
|
||||
* Provides a mechanism to remove rules from the rate limiter
|
||||
* Provides a mechanism to remove rules from the rate limiter. Returns boolean
|
||||
* about success.
|
||||
* @param {string} id Rule id returned from #addRule
|
||||
* @return {boolean} Returns true if rule was found and deleted, else false.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user