Bola Idor in Phoenix with Hmac Signatures
Bola Idor in Phoenix with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) occurs when an API fails to enforce proper ownership or authorization checks between a reference ID and the requesting user. In Phoenix applications that use Hmac Signatures for request authentication, BOLA can still arise because the Hmac verifies the request integrity and origin, but does not by itself validate whether the authenticated actor is allowed to access the specific resource identified in the URL or query parameters.
Consider a typical Phoenix endpoint that retrieves a user profile by ID: /api/users/123. If the request uses a query parameter or header containing an Hmac signature generated over the path and a shared secret, the server can confirm the request has not been tampered with and may trust the embedded user identifier. However, if the server uses the ID directly to fetch the record without confirming that the authenticated principal owns ID 123, an attacker who knows or guesses another user’s ID can read, update, or delete that resource. This is a classic BOLA/IDOR scenario: authorization is missing at the object level even though the request itself is cryptographically verified.
In practice, this vulnerability is often exposed when developers conflate authentication with authorization. Hmac Signatures can authenticate that the request came from a trusted client and that parameters like timestamps and nonces are intact, but they do not embed user context or permissions. If the endpoint relies on the ID present in the URL to decide which data to return, and that ID is not cross-checked against the authenticated subject (for example, from a session or an access token), the attack surface mirrors IDOR. Common patterns include using an Hmac over GET /api/invoices/:id where the invoice ID is not validated against the current user’s allowed invoices, or using Hmac-signed query parameters to skip pagination or filtering checks that would otherwise limit visibility to the actor’s own records.
Additional risk patterns arise when IDs are predictable (sequential or UUIDs without sufficient entropy) and Hmac signatures do not bind the request to a tenant or scope. An attacker can systematically iterate through IDs, sending each one with a valid Hmac (if a weak key management scheme allows key leakage or if the signature is reused), and observe differences in response behavior or data returned. Even when the Hmac includes a timestamp and nonce to prevent replay, missing server-side ownership checks mean that each crafted request succeeds, leading to unauthorized data exposure or manipulation. This combination is particularly dangerous in administrative or multi-tenant systems where different user groups share similar endpoint shapes but must be strictly separated by data ownership.
To identify this using middleware analysis, consider how the request flows from signature verification to data access. If the verified parameters are passed directly to a query like Repo.get!(User, id) without scoping to the authenticated user, the BOLA condition is present. The presence of Hmac Signatures does not mitigate this; it only ensures the parameters have not been altered, not that they are permissible for the actor. Real-world examples align with findings from scans that detect missing authorization checks even when cryptographic integrity is enforced, highlighting that BOLA is about authorization granularity, not transport integrity.
Hmac Signatures-Specific Remediation in Phoenix — concrete code fixes
Remediation focuses on ensuring that after Hmac verification, every data access is scoped to the authenticated subject and that object-level policies are enforced explicitly. Do not rely on the signature to convey user identity; instead, derive the subject from a trusted session or token and use it to constrain queries. Below are concrete, idiomatic examples in Phoenix/Elixir that demonstrate secure patterns.
Example 1: Scoped data access with verified Hmac inputs
Assume you verify an Hmac in a plug that extracts and validates parameters. After validation, you should look up the current user from the session or token and scope the resource lookup to that user.
defmodule MyAppWeb.UserProfilePlug do
import Plug.Conn
alias MyApp.Accounts
alias MyApp.Accounts.User
def init(opts), do: opts
def call(conn, _opts) do
with { :ok, %{ "user_id" => user_id, "timestamp" => ts, "nonce" => nonce } } <- verify_hmac(conn),
%User{} = current_user <- Accounts.get_user_for_authorization(conn, user_id) do
# Attach the verified subject to the connection for downstream use
assign(conn, :current_user, current_user)
else
_ -> send_resp(conn, 401, "Unauthorized") |> halt()
end
end
defp verify_hmac(conn) do
# Extract signed params, validate timestamp/nonce, ensure replay protection
# Return {:ok, params} or :error
# This is a placeholder for your actual Hmac verification logic
...
end
end
In the data layer, scope by the authenticated user ID instead of trusting the raw ID from the request parameters:
defmodule MyApp.Accounts do
alias MyApp.Repo
alias MyApp.Accounts.User
def get_user_for_authorization(conn, requested_id) do
user_id = conn.assigns.current_user.id
# Enforce ownership: only allow access if requested_id matches the authenticated user
if user_id == requested_id do
Repo.get!(User, requested_id)
else
nil
end
end
end
Example 2: Policy-based authorization with a library like Bodyguard or Pundit
Use a policy module to centralize object-level decisions. After Hmac verification, authorize each action against the subject and the resource.
defmodule MyAppWeb.UserController do
use MyAppWeb, :controller
alias MyApp.Accounts
def show(conn, %{"id" => id}) do
user = Accounts.get_user_for_authorization(conn, id)
case UserPolicy.authorize(conn.assigns.current_user, user, :show?) do
{:ok, _user} -> json(conn, %{data: user})
{:error, _} -> conn |> put_status(:forbidden) |> json(%{error: "Forbidden"})
end
end
end
defmodule MyApp.Policies.UserPolicy do
def authorize(%{id: subject_id}, %Ecto.Schema{id: resource_id}, _action) when subject_id == resource_id, do: {:ok, %{}}
def authorize(_subject, _resource, _action), do: {:error, :unauthorized}
end
Additional hardening recommendations
- Do not embed user identifiers inside Hmac-signed parameters that are used directly as resource IDs; keep identity separate from integrity-protected data.
- Use constant-time comparison for any shared secrets and rotate keys periodically.
- Combine Hmac verification with rate limiting and logging to detect enumeration attempts that could indicate probing for valid IDs.
- Ensure that your authorization logic is applied consistently across all endpoints that accept user-controlled IDs, including nested resources (e.g.,
/api/organizations/:org_id/projects/:project_id), where both org and project ownership must be validated against the subject.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |