apiac_filter_throttler v1.0.0 APIacFilterThrottler View Source

An APIac.Filter plug for API requests rate-limiting

This plug uses the Exhammer package as its backend. This library uses the token bucket algorithm, which means that this plug is mainly suitable for limiting abuses, not for accurate rate limiting. By default, a local ETS backend is launched on startup.

Plug options

  • key: a (Plug.Conn.t -> String.t | {String.t, non_neg_integer(), non_neg_integer()}) function, taking in parameter the connection and returning either the key, or the tuple {key, scale, limit}. No default value. Note that the APIacFilterThrottler.Functions provides with out-of-the-box functions
  • scale: the time window of the token bucket algorithm, in milliseconds. No default value.
  • limit: the maximum limit of the token bucket algorithm, in attempt count. No default value.
  • increment: the increment of the token bucket algorithm (defaults to 1)
  • backend: Exhammer's backend, defaults to nil
  • exec_cond: a (Plug.Conn.t() -> boolean()) function that determines whether this filter is to be executed or not. Defaults to fn _ -> true end
  • send_error_response: function called when request is throttled. Defaults to Elixir.APIacFilterThrottler.send_error_response/3
  • error_response_verbosity: one of :debug, :normal or :minimal. Defaults to :normal

Example

Allow 50 request / 10 seconds per subject and per client:

plug APIacFilterThrottler, key: &APIacFilterThrottler.Functions.throttle_by_subject_client/1,
  scale: 10_000,
  limit: 50

Allow 5000 requests / minute per client, only for machine-to-machine access:

plug APIacFilterThrottler, key: &APIacFilterThrottler.Functions.throttle_by_client/1,
  exec_cond: &APIac.machine_to_machine?/1,
  scale: 60_000,
  limit: 5000

Security considerations

Consider the risk of collisions when constructing the key> For instance, a key function concatenating the ip address and a subject (username) would return the same key ("72.23.241.121edwards") for:

  • a user "edwards" connecting from 72.23.241.121
  • a user "1edwards" connecting from 72.23.241.12

The more control an attacker has on choosing the key parameters (e.g. the username), the easier to find a collision.

Finding a collision can result in a DOS for the legitimate requester.

Using a hash function such as :erlang.phash2/1, MD5, etc. cam help mitigate the risk, at the expense of performance. Also note that :erlang.phash2/1 is not a collision-resistant hash function (as results are not uniformly distributed).

Link to this section Summary

Link to this section Functions

Link to this function

send_error_response(conn, error, opts)

View Source

Implementation of the APIac.Filter behaviour.

Verbosity

The following elements in the HTTP response are set depending on the value of the :error_response_verbosity option:

Error reponse verbosityHTTP statusHeadersBody
:debugToo many requests (429)retry-afterAPIac.Filter.Forbidden exception's message
:normalToo many requests (429)retry-after
:minimalForbidden (403)