PhoenixGenApi.Security (PhoenixGenApi v2.18.0)

Copy Markdown View Source

Provides security utilities for PhoenixGenApi.

Features

  1. Admin gate — fail-closed authorization for dangerous runtime operations (toggling detail_error, updating rate limit config, pushing configs).

  2. Push token validation — constant-time comparison to authenticate push requests from remote nodes.

  3. MFA allowlist validation — restricts which {module, function, args} tuples can be registered as function configurations, preventing a compromised node from registering dangerous MFAs (e.g. :os.cmd).

Configuration

All checks are opt-in and backward compatible. If the relevant application environment variables are not set, the checks are skipped:

config :phoenix_gen_api,
  # Admin actions allowlist (fail-closed: default denies everything)
  admin_actions: [:push_config],

  # Push token — when set, push requests must include a matching token
  push_token: "my-secret-token",

  # MFA allowlist — when set, only listed {module, function} pairs are allowed.
  # Module-level entries (just an atom) allow all functions in that module.
  mfa_allowlist: [
    MyApp.UserService,
    {MyApp.OrderService, :create_order}
  ]

Hardcoded Denylist

The following modules are always blocked unless explicitly allowed: :os, :file, :code, :erlang, :net, :rpc, :global, :inet.

Environment Recommendations

  • Development: You may enable all admin actions for convenience.
  • Production: Enable only what's needed. Configure push_token and mfa_allowlist to restrict push sources and allowed function targets.

Summary

Functions

Checks whether a given admin action is currently permitted.

Validates a push token using constant-time comparison.

Validates that an MFA tuple is allowed by the configured allowlist and not in the hardcoded denylist.

Types

admin_action()

@type admin_action() ::
  :change_detail_error | :update_rate_limit_config | :push_config

Functions

admin_action_allowed?(action)

@spec admin_action_allowed?(admin_action()) :: boolean()

Checks whether a given admin action is currently permitted.

Returns true if the action is in the configured allowlist, false otherwise. When denied, a warning is logged.

Examples

iex> PhoenixGenApi.Security.admin_action_allowed?(:update_rate_limit_config)
false

iex> PhoenixGenApi.Security.admin_action_allowed?(:change_detail_error)
false

valid_push_token?(token)

@spec valid_push_token?(nil | String.t() | binary()) :: boolean()

Validates a push token using constant-time comparison.

Returns true if:

  • No :push_token is configured (backward compatible — push allowed without token)
  • The provided token matches the configured token

Returns false if a token is configured but the provided token doesn't match or is missing.

Uses constant-time comparison to prevent timing attacks.

validate_mfa(mfa)

@spec validate_mfa({module(), atom(), list()}) ::
  :ok | {:error, {:mfa_not_allowed, term()}}

Validates that an MFA tuple is allowed by the configured allowlist and not in the hardcoded denylist.

Parameters

  • mfa - A {module, function, args} tuple

Returns

  • :ok if the MFA is allowed
  • {:error, {:mfa_not_allowed, mfa}} if the MFA is not allowed

Behavior

  • If no :mfa_allowlist is configured, all MFAs pass the allowlist check (backward compatible) — but the hardcoded denylist is still enforced.
  • If :mfa_allowlist IS configured, the {module, function} pair must match an entry. Entries can be:
    • A module atom (e.g. MyApp.UserService) — allows all functions in that module
    • A {module, function} tuple (e.g. {MyApp.OrderService, :create_order})
  • Modules in the hardcoded denylist (:os, :file, :code, :erlang, :net, :rpc, :global, :inet) are ALWAYS blocked unless the allowlist explicitly includes them.