HIGH broken access controlphoenixapi keys

Broken Access Control in Phoenix with Api Keys

Broken Access Control in Phoenix with Api Keys — how this specific combination creates or exposes the vulnerability

Broken Access Control occurs when authorization checks are missing, incomplete, or bypassed, allowing an authenticated user to access or modify resources they should not. In Phoenix applications that rely on API keys for authentication, this risk is pronounced because API keys are often treated as static secrets without sufficient scoping or ownership checks. If a key is issued for a tenant or user but the endpoint does not validate that the requesting key maps to the resource being accessed, an attacker can manipulate identifiers (e.g., changing an ID in the URL) and access data or perform actions belonging to another tenant or user. This is a classic BOLA/IDOR scenario intertwined with weak authorization tied to API keys.

Phoenix typically uses plug pipelines to authenticate requests. When API keys are used, developers might authenticate the request by verifying the key against a database but then skip or incorrectly implement per-resource authorization. For example, an endpoint like /api/organizations/:org_id/members might authenticate the key and fetch the organization record, but if the code does not ensure that the key’s associated organization matches org_id, an attacker can iterate IDs to view or modify other organizations. This is exacerbated when keys are long-lived and leaked in logs or client-side code, increasing exposure. The absence of rate limiting and insufficient audit logging further enable enumeration and abuse.

The 12 parallel security checks in middleBrick highlight these risks for Phoenix APIs. Unauthenticated scanning can detect endpoints that accept API keys but still expose BOLA/IDOR or improper property authorization. Input validation checks reveal whether IDs and parameters are sanitized and constrained, while Authentication and Unsafe Consumption checks assess how keys are accepted and stored in requests. Because middleBrick performs black-box testing using OpenAPI specs and runtime analysis, it can surface mismatches between declared scopes in the spec and actual runtime behavior, such as missing authorization checks on sensitive paths. These findings map to OWASP API Top 10 A01:2023 — Broken Access Control and align with relevant PCI-DSS and SOC2 controls, emphasizing the need for robust authorization tied to API keys.

Api Keys-Specific Remediation in Phoenix — concrete code fixes

Remediation centers on ensuring that every authorized request not only presents a valid API key but also that the key is explicitly linked to the resource being accessed. Store keys with metadata such as tenant ID or user ID, and enforce ownership checks in your pipelines or controllers. Avoid relying solely on plug-based authentication without subsequent authorization. Below are concrete examples using Phoenix controllers and pipelines that demonstrate how to bind an API key to a resource and validate access.

First, define a schema and changeset for API keys with an associated tenant identifier:

defmodule MyApp.Accounts.ApiKey do
  use Ecto.Schema

  schema "api_keys" do
    field :key, :string
    field :tenant_id, :id
    field :scopes, {:array, :string}, default: []
    timestamps()
  end

  def changeset(api_key, attrs) do
    api_key
    |> Ecto.Changeset.cast(attrs, [:key, :tenant_id, :scopes])
    |> Ecto.Changeset.validate_required([:key, :tenant_id])
  end
end

Next, create a pipeline that loads the key and assigns the tenant, ensuring the key exists and is active:

defmodule MyAppWeb.ApiKeyPipeline do
  import Plug.Conn
  import MyAppWeb.Gettext

  def init(opts), do: opts

  def call(conn, _opts) do
    case get_api_key(conn) do
      {:ok, api_key} ->
        # Attach tenant context to the connection
        assign(conn, :current_tenant_id, api_key.tenant_id)

      {:error, _reason} ->
        conn
        |> put_status(:unauthorized)
        |> json(%{error: gettext("Invalid API key")})
        |> halt()
    end
  end

  defp get_api_key(conn) do
    api_key = conn
              |> get_req_header("authorization")
              |> List.first()
              |> String.replace("ApiKey ", "")

    if api_key && byte_size(api_key) > 0 do
      case MyApp.Accounts.get_api_key_by_key(api_key) do
        %MyApp.Accounts.ApiKey{} = key -> {:ok, key}
        nil -> {:error, :invalid}
      end
    else
      {:error, :missing}
    end
  end
end

In your controller, use the assigned tenant ID to scope queries, preventing BOLA/IDOR regardless of which API key is presented:

defmodule MyAppWeb.OrganizationController do
  use MyAppWeb, :controller
  alias MyApp.Organizations
  alias MyApp.Organizations.Organization

  plug MyAppWeb.ApiKeyPipeline when action in [:index, :show, :update]

  def index(conn, _params) do
    tenant_id = conn.assigns.current_tenant_id
    organizations = Organizations.list_organizations_for_tenant(tenant_id)
    render(conn, :index, organizations: organizations)
  end

  def show(conn, %{"id" => id}) do
    tenant_id = conn.assigns.current_tenant_id
    with %Organization{} = organization <- Organizations.get_organization_by_tenant(id, tenant_id) do
      render(conn, :show, organization: organization)
    else
      nil -> send_resp(conn, :not_found, "Not found")
    end
  end
end

The critical part is get_organization_by_tenant, which includes the tenant ID in the query, ensuring that even if an attacker guesses or iterates an ID, they cannot access organizations outside their tenant. Combine this with proper key rotation, scoped keys, and audit logging. middleBrick’s Pro plan supports continuous monitoring and can integrate into your CI/CD pipeline to fail builds if risk scores degrade, helping you maintain secure authorization over time.

Frequently Asked Questions

What is the difference between authentication and authorization in the context of API keys in Phoenix?
Authentication verifies that the API key is valid and issued by the system; authorization ensures the key’s scope and tenant context permit access to the specific resource. Broken access control often arises when authentication succeeds but authorization checks are missing or bypassed.
How can I test my Phoenix endpoints for BOLA/IDOR related to API keys?
Use black-box scanning tools like middleBrick to probe endpoints without authentication, then inspect findings related to BOLA/IDOR and property authorization. Combine automated scans with targeted manual tests that manipulate resource identifiers while using different valid API keys belonging to other tenants.