HIGH broken access controlphoenixmutual tls

Broken Access Control in Phoenix with Mutual Tls

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

Broken Access Control occurs when authorization checks are missing or bypassed, allowing a subject to access resources or perform actions they should not. In Phoenix applications that use Mutual TLS (mTLS), developers can mistakenly believe that mTLS alone is sufficient authorization. While mTLS provides strong authentication of the client identity, it does not enforce what an authenticated identity is allowed to do. If authorization logic is missing or incorrectly applied, an attacker who possesses a valid client certificate can access any endpoint that lacks proper access controls.

Consider a Phoenix API with endpoints such as /api/users/:user_id and /api/admin/settings. If the API terminates mTLS at the transport layer and maps the client certificate’s subject or Common Name (CN) to a user identity, but does not verify that the requesting user is allowed to view or modify the target user_id, this is a Broken Access Control flaw (BOLA/IDOR). Insecure Direct Object References occur when user-supplied input (e.g., user_id) is used directly to query the database without verifying that the authenticated subject owns that resource.

With mTLS, the client certificate is presented during the TLS handshake, and Phoenix can extract identity information from the certificate. Typical extraction looks at the certificate’s Subject Alternative Name (SAN) or Distinguished Name (DN). For example, you might configure Plug to read a custom extension or the CN to determine the current user. However, if the application uses this identity solely for authentication and omits per-request authorization checks, the unauthenticated attack surface includes scenarios where one user assumes another’s identity simply by changing the ID in the request.

Even with mTLS, missing rate limiting and weak inventory management can amplify the impact. An attacker could iterate over numeric user IDs without triggering account lockouts or alerts, especially if rate limiting is not enforced at the API level. Data exposure then occurs because sensitive user data is returned without verifying that the authenticated subject has the right to access it. This aligns with OWASP API Top 10 A01: Broken Object Level Authorization and maps to compliance frameworks such as OWASP API Top 10, SOC2, and GDPR.

middleBrick detects this class of issue by correlating mTLS authentication findings with missing authorization checks across the 12 security checks, including Authentication, BOLA/IDOR, and Property Authorization. The scanner validates that identity derived from mTLS is consistently used in authorization logic and that endpoints enforce scoping (e.g., ensuring a user can only access their own resources). This helps highlight where additional controls are required beyond transport-layer identity.

Mutual Tls-Specific Remediation in Phoenix — concrete code fixes

To remediate Broken Access Control in Phoenix with mTLS, you must enforce explicit authorization after successful client authentication. Do not rely on mTLS alone for authorization. Use the identity extracted from the client certificate to scope every data access and enforce ownership checks.

1. Extract identity from client certificate in a plug

Define a plug that reads the client certificate from the connection and assigns a normalized subject to assigns. This example reads a SAN or a specific DN field and ensures the value is present before proceeding.

defmodule MyAppWeb.CertificateAuth do
  @moduledoc """
  Extract client certificate identity and validate presence.
  """
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    case get_client_certificate(conn) do
      {:ok, cert_subject} ->
        # Attach identity to assigns for downstream use
        assign(conn, :cert_subject, cert_subject)

      {:error, :missing_cert} ->
        # Reject unauthenticated requests early
        send_resp(conn, 401, "Client certificate required")
        |> halt()
    end
  end

  defp get_client_certificate(conn) do
    case conn.client_cert do
      nil -> {:error, :missing_cert}
      cert ->
        # Parse the certificate and extract subject or SAN
        {:ok, extract_subject(cert)}
    end
  end

  defp extract_subject(cert) do
    # Use :public_key.pkix_decode_cert/2 to parse DER and extract CN or SAN
    # This is a simplified placeholder
    "user-123"
  end
end

2. Enforce ownership in controllers and policies

After assigning :cert_subject, use it to scope queries. For example, when fetching a user profile, ensure the requested ID matches the authenticated subject.

defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller
  alias MyApp.Accounts
  plug MyAppWeb.CertificateAuth

  def show(conn, %{"id" => user_id}) do
    cert_subject = conn.assigns.cert_subject
    # Ensure the requested user matches the certificate subject
    with true <- authorized?(cert_subject, user_id),
         user <- Accounts.get_user_for_subject(cert_subject, user_id) do
      render(conn, "show.json", user: user)
    else
      false -> send_resp(conn, 403, "Forbidden")
      nil -> send_resp(conn, 404, "Not found")
    end
  end

  defp authorized?(cert_subject, user_id) do
    # Implement scoping: cert_subject must map to the user_id
    # Example: cert_subject == "user-#{user_id}" or lookup mapping
    "user-" <> id = cert_subject
    id == user_id
  end
end

3. Apply consistent policy checks across endpoints

For sensitive operations such as admin settings, enforce explicit role or scope checks using the certificate identity. Do not assume mTLS implies admin rights.

defmodule MyAppWeb.AdminController do
  use MyAppWeb, :controller
  plug MyAppWeb.CertificateAuth

  def settings(conn, _params) do
    cert_subject = conn.assigns.cert_subject
    if admin_subject?(cert_subject) do
      settings = MyApp.Config.settings()
      render(conn, "settings.json", settings: settings)
    else
      send_resp(conn, 403, "Admin access required")
    end
  end

  defp admin_subject?(cert_subject) do
    # Map certificate to admin status via DB or allow-list
    case MyApp.Accounts.lookup_cert_role(cert_subject) do
      {:ok, "admin"} -> true
      _ -> false
    end
  end
end

4. Combine mTLS with application-level authorization

Use a policy library to centralize authorization decisions. This ensures that even if mTLS identity is available, each endpoint validates permissions explicitly.

defmodule MyAppWeb.GrantController do
  use MyAppWeb, :controller
  plug MyAppWeb.CertificateAuth

  def create(conn, %{"grant" => grant_params}) do
    cert_subject = conn.assigns.cert_subject
    case MyApp.Grants.create_grant(cert_subject, grant_params) do
      {:ok, grant} ->
        conn
        |> put_status(:created)
        |> render("grant.json", grant: grant)
      {:error, :forbidden} ->
        send_resp(conn, 403, "Not authorized to create grants")
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(MyAppWeb.ChangesetView, "error.json", changeset: changeset)
    end
  end
end

5. Operational practices

  • Ensure your load balancer or proxy terminates mTLS and forwards client certificate details securely to Phoenix when necessary, or let Phoenix handle termination directly.
  • Map certificate identities to user roles in a reliable source of truth (e.g., database or directory) and keep mappings up to date.
  • Log authorization failures for audit and monitoring, but avoid logging full certificate details to prevent accidental exposure.

By combining mTLS authentication with explicit, per-request authorization checks, you eliminate the risk of Broken Access Control and BOLA/IDOR in Phoenix applications. middleBrick validates that authentication and authorization are both present and aligned, reducing the likelihood of insecure direct object references.

Frequently Asked Questions

Does mTLS alone prevent Broken Access Control in Phoenix APIs?
No. Mutual TLS provides strong client authentication but does not enforce authorization. You must implement explicit access controls and scope data access to prevent Broken Access Control and BOLA/IDOR.
How can I verify that my Phoenix endpoints enforce proper authorization alongside mTLS?
Use a security scanner to correlate authentication (mTLS) with authorization checks across endpoints. Ensure each endpoint that uses mTLS identity validates ownership and permissions before returning data; remediate by adding per-request policy checks and scoping queries to the authenticated subject.