FastSitePHP\Security\Web\RateLimit

Rate limiting can be used to limit the number of requests or actions that a user makes in a given time frame.

Some examples:
  - A Web API allowing users to submit no more than 1 request every second.
  - No more than 2 new accounts per day per IP address.
  - Limit users from sending more than 10 messages per hour.

FastSitePHP's Rate Limit class is designed to provide an easy-to-use interface for defining and enforcing rate limits.

Source Code

GitHub

Example Code

Security - Rate Limiting

// Rate Limit Class
$rate_limit = new \FastSitePHP\Security\Web\RateLimit();

// Using the RateLimit class requires an instance of
// [\FastSitePHP\Data\KeyValue\StorageInterface].
// In this example SQLite is used. When multiple servers are used behind
// a load balancer an in-memory cache db such as Redis can be used.
$file_path = sys_get_temp_dir() . '/ratelimit-cache.sqlite';
$storage = new \FastSitePHP\Data\KeyValue\SqliteStorage($file_path);

// There are 2 required options [storage] and [id].
// [id] represents the user - IP Address, User ID, etc.
//
// [max_allowed] and [duration] will commonly be used and represent
// the rate at which the event is allowed. If not specified then a
// default of 1 is used which allows for 1 request per second.
$options = [
    'max_allowed' => 1, // Requests, Events, etc
    'duration' => 1, // In seconds
    'storage' => $storage,
    'id' => $_SERVER['REMOTE_ADDR'],
];

// Check the Request
list($allowed, $headers) = $rate_limit->allow($options);
// $allowed = bool
// $headers = Array of headers that can be used for logic
//            or sent with the response

// One thing to be aware of when filtering by IP is that many users can have
// the same IP if they are accessing your site from the same office or location.

// Option examples:

// Limit to 10 requests every 20 seconds
$options = [ 'max_allowed' => 10, 'duration' => 20, ];

// Limit to 2 requests per minute
$options = [ 'max_allowed' => 2, 'duration' => 60, ];

// Limit to 2 requests per day
$options = [ 'max_allowed' => 10, 'duration' => (60 * 60 * 24), ];

// If using the [RateLimit] class for multiple
// uses then you need to specify an optional key.
$options = [ 'key' => 'messages-sent' ];
$options = [ 'key' => 'accounts-created' ];

// The [RateLimit] class allows for different rate limiting algorithms;
// the default is 'fixed-window-counter' which puts a fixed amount on
// the number of requests for the given duration, but allows for bursts.
// The 'token-bucket' allows for rate limiting at a timed rate however
// it can allow for a higher number requests than the specified [max_allowed].
//
// For basic usage with a small number of [max_allowed] such as
// "1 request per second" they will behave the same, however if specifying
// a larger number such as "10 requests per 20 seconds" then there will
// be a difference so if you are using rate limiting for web requests with
// a larger number you may want to compare the differences using example code
// and see related links in the API docs.
//
$options = [ 'algo' => 'fixed-window-counter' ];
$options = [ 'algo' => 'token-bucket' ];

// The [filterRequest()] function can be used to filter the request.
// When used if the user's rate limit is reached then a 429 [Too Many Requests]
// response is sent and [exit()] is called to stop the script execution.
$filter_request = function() use ($app, $storage) {
    // Get User IP (example if using a load-balancer)
    $req = new \FastSitePHP\Web\Request();
    $user_ip = $req->clientIp('from proxy');

    // Check rate
    $rate_limit = new \FastSitePHP\Security\Web\RateLimit();
    $rate_limit->filterRequest($app, [
        'storage' => $storage,
        'id' => $user_ip,
    ]);
};
$app->get('/api', function() {})->filter($filter_request);

// When using [filterRequest()] the following Response Headers can sent
// to the client depending on which options are used:
//   Retry-After            Standard Header
//   X-RateLimit-Limit      Human readable description of the rate limit
//   X-RateLimit-Remaining  Requests allowed for the given time frame
//   X-RateLimit-Reset      Unix Timestamp for the limit to reset

Methods

filterRequest(Application $app, array $options)

Filter the request if it is allowed based on a rate limit. If the user's rate limit is reached then a 429 [Too Many Requests] response is sent and [exit()] is called to stop the script execution.

The same options used for [allow()] are used here.

allow(array $options)

Check if a request or action is allowed based on a rate limit.

Required Options:
    - [storage]: Object - Instance of [FastSitePHP\Data\KeyValue\StorageInterface]
    - [id]: Id assigned to the user or request. For example the client's IP Address or a user id

Common Optional Options:
    - [max_allowed] (int): Maximum number of requests allowed for the specified duration
    - [duration] (int): Time in sections

Additional Options:
    - [key]: String value to prefix when saving a key-value-pair.
      This would be used if you are using the RateLimiter for multiple actions in the same site.
    - [algo]: Algorithm to use ['fixed-window-counter' or 'token-bucket']. Defaults to 'fixed-window-counter'.

Returns: array - list($allowed, $headers)