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.
def deps do
[
{:apiac_filter_throttler, "~> 1.0"}
]
end
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 theAPIacFilterThrottler.Functions
provides with out-of-the-box functionsscale
: 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 to1
)backend
: Exhammer's backend, defaults tonil
exec_cond
: a(Plug.Conn.t() -> boolean())
function that determines whether this filter is to be executed or not. Defaults tofn _ -> true end
send_error_response
: function called when request is throttled. Defaults toAPIacFilterThrottler.send_error_response/3
error_response_verbosity
: one of:debug
,:normal
or:minimal
. Defaults to:normal
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
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).