PlugAttack behaviour (plug_attack v0.4.3)

A plug building toolkit for blocking and throttling abusive requests.

PlugAttack is a set of macros that can be used to build a plug to protect your web app from bad clients. It allows safelisting, blocklisting and throttling based on arbitrary properties of the request.

The throttling state is stored in a configurable storage. By default an implementation backed by :ets tables is offered.

Example

defmodule MyApp.PlugAttack do
  import Plug.Conn
  use PlugAttack

  # For more rules examples see PlugAttack.rule/2 macro documentation.
  rule "allow local", conn do
    allow conn.remote_ip == {127, 0, 0, 1}
  end

  # It's possible to customize what happens when conn is let through
  def allow_action(conn, _data, _opts), do: conn

  # Or when it's blocked
  def block_action(conn, _data, _opts) do
    conn
    |> send_resp(:forbidden, "Forbidden\n")
    |> halt
  end
end

Link to this section Summary

Types

The rule return value.

Callbacks

Action performed when the request is allowed.

Action performed when the request is blocked.

Link to this section Types

Specs

rule() :: {:allow, term()} | {:block, term()} | nil

The rule return value.

Link to this section Callbacks

Link to this callback

allow_action(conn, term, term)

Specs

allow_action(conn :: Plug.Conn.t(), term(), term()) :: Plug.Conn.t()

Action performed when the request is allowed.

Link to this callback

block_action(conn, term, term)

Specs

block_action(conn :: Plug.Conn.t(), term(), term()) :: Plug.Conn.t()

Action performed when the request is blocked.

Link to this section Functions

Link to this macro

rule(message, var \\ quote do _ end, contents)

(macro)

Defines a rule.

A rule is an expression that returns either {:allow, data}, {:block, data}, or nil. If an allow or block tuple is returned we say the rule matched, otherwise the rule didn't match and further rules will be evaluated.

If a rule matched the corresponding allow_action/3 or block_action/3 function on the defining module will be called passing the conn, the data value from the allow or block tuple and opts as returned by the init/1 plug callback. If none rule matched, neither allow_action/3 nor block_action/3 will be called.

Both actions should behave similarly to plugs, returning the modified conn argument. The default implementation of allow_action/3 will return the conn unmodified. The default implementation of block_action/3 will respond with status 403 Forbidden, the body "Forbidden\n" and halt the plug pipeline.

Various predefined rules are defined in the PlugAttack.Rule module. This module is automatically imported in the rule's body.

Examples

rule "allow local", conn do
  allow conn.remote_ip == {127, 0, 0, 1}
end

rule "block 1.2.3.4", conn do
  block conn.remote_ip == {1, 2, 3, 4}
end

rule "throttle per ip", conn do
  # throttle to 5 requests per second
  throttle conn.remote_ip,
    period: 1_000, limit: 5,
    storage: {PlugAttack.Storage.Ets, MyApp.PlugAttack.Storage}
end

rule "throttle login requests", conn do
  if conn.method == "POST" and conn.path_info == ["login"] do
    throttle conn.params["email"],
      period: 60_000, limit: 10,
      storage: {PlugAttack.Storage.Ets, MyApp.PlugAttack.Storage}
  end
end