HIGH bola idorphoenixbearer tokens

Bola Idor in Phoenix with Bearer Tokens

Bola Idor in Phoenix with Bearer Tokens — how this combination creates or exposes the vulnerability

Broken Object Level Authorization (BOLA) occurs when an API fails to enforce proper access controls between a user and a specific resource instance. In a Phoenix application that uses Bearer Tokens for authentication, this risk emerges when authorization checks are incomplete or inconsistent across endpoints. Even when a request presents a valid Bearer Token, the server may identify the user but then fail to verify that the requested resource (e.g., a task, a profile, or an invoice) belongs to that user.

Consider a typical Phoenix API route like /api/users/:user_id/tasks/:id. Authentication confirms the caller owns a Bearer Token and maps that token to a user ID. However, if the controller loads the task by ID without confirming that the task’s user_id matches the authenticated user’s ID, an attacker can manipulate the :id parameter to access or modify another user’s tasks. This is BOLA: the object-level guard is missing or misapplied.

When Bearer Tokens are involved, the issue is often not about token validity but about how the backend uses the identity derived from the token. Phoenix APIs commonly rely on Guardian or Pow for token parsing, which sets assigns like current_user. If subsequent queries use parameters (e.g., account_id or document_id) without reconfirming ownership against current_user, the API implicitly trusts client-supplied identifiers. This trust enables horizontal privilege escalation, where one user accesses another’s data, and vertical escalation, where a user gains access to administrative objects they should not reach.

Real-world patterns that amplify BOLA with Bearer Tokens include concatenating user-supplied IDs into Ecto queries without scoping by the authenticated user, using path parameters that are not validated against the token’s subject, or exposing internal IDs that leak relationships across tenants. For example, an endpoint like GET /api/organizations/:org_id/members/:member_id that only checks that a Bearer Token exists, but not that the member belongs to the provided org_id, allows enumeration and tampering across organizations. Inadequate logging and monitoring also mean these mismatches can persist without detection.

The OWASP API Top 10 categorizes this as Broken Object Level Authorization, and it frequently intersects with other risks such as Excessive Data Exposure and Improper Asset Management. In a security scan, such flaws are flagged with high severity because they enable unauthorized read and write operations on user-specific resources. Remediation requires consistently coupling authentication (via Bearer Tokens) with strict ownership checks at the data layer, ensuring every object operation validates the relationship between the token’s subject and the target resource.

Bearer Tokens-Specific Remediation in Phoenix — concrete code fixes

To fix BOLA in a Phoenix API that uses Bearer Tokens, always scope queries to the authenticated subject derived from the token. Do not rely on route parameters alone to determine access rights. Below are concrete, idiomatic examples using Guardian for token verification and Ecto for safe data access.

Example: Scoped task retrieval

Instead of loading a task by ID and assuming the requester owns it, scope the query to the authenticated user. With Guardian, current_user is typically set in a pipeline or plug after token validation.

defmodule MyAppWeb.TaskController do
  use MyAppWeb, :controller

  alias MyApp.Tasks
  alias MyApp.Tasks.Task

  def show(conn, %{"id" => task_id}) do
    case Tasks.get_user_task(conn.assigns.current_user.id, task_id) do
      nil -> send_resp(conn, 404, "Not found")
      task -> render(conn, "task.json", task: task)
    end
  end
end

The corresponding context function ensures the task belongs to the user:

defmodule MyApp.Tasks do
  import Ecto.Query, warn: false
  alias MyApp.Repo
  alias MyApp.Tasks.Task

  def get_user_task(user_id, task_id) do
    Task
    |> where([t], t.id == ^task_id and t.user_id == ^user_id)
    |> Repo.one()
  end
end

Organization membership validation

For endpoints that involve associations such as organizations, verify membership explicitly rather than trusting the provided organization ID.

defmodule MyAppWeb.OrganizationController do
  use MyAppWeb, :controller

  def show(conn, %{"org_id" => org_id}) do
    with true <- MyApp.Organizations.user_member?(conn.assigns.current_user.id, org_id) do
      org = MyApp.Organizations.get_organization(org_id)
      render(conn, "org.json", org: org)
    else
      false -> {:error, :forbidden}
    end
  end
end

And the check in the context:

defmodule MyApp.Organizations do
  import Ecto.Query, warn: false
  alias MyApp.Repo
  alias MyApp.Organizations.UserOrganization

  def user_member?(user_id, org_id) do
    UserOrganization
    |> where([uo], uo.user_id == ^user_id and uo.organization_id == ^org_id)
    |> Repo.exists?()
  end
end

Pipeline and plug-based enforcement

Centralize authorization checks in plugs so that every route that receives a Bearer Token enforces ownership at the point of data access. This reduces the chance of accidentally omitting a guard in a new endpoint.

defmodule MyAppWeb.Plugs.RequireOwnership do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    resource_id = conn.params["id"] || conn.params["org_id"]
    user = conn.assigns.current_user

    if resource_id && user && resource_exists_for_user?(user.id, resource_id) do
      conn
    else
      conn
      |> put_status(403)
      |> json(%{error: "Forbidden"})
      |> halt()
    end
  end

  defp resource_exists_for_user?(user_id, resource_id) do
    # Implement a generic or specific check depending on route patterns
    # e.g., scoped Ecto query or policy function
  end
end

By consistently combining Bearer Token authentication with explicit, scoped queries and plug-level checks, you eliminate the conditions that enable BOLA. This approach keeps authorization close to the data layer and avoids relying on the client to indicate which objects a user may access.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

How can I test if my Phoenix API has BOLA with Bearer Tokens?
Use an API security scanner that supports authenticated and unauthenticated checks, such as middleBrick. Submit your endpoint URL; it will run unauthenticated attack surface tests including BOLA/IDOR checks and report whether objects are accessible across users without proper ownership verification.
Does using Bearer Tokens alone prevent BOLA?
No. Bearer Tokens authenticate identity but do not enforce object-level permissions. BOLA can still occur if endpoints do not scope data access to the authenticated subject. Always couple tokens with scoped queries and explicit ownership checks.