Skip to main content
Vulnerability Database/CVE-2026-82724

CVE-2026-82724: Ash Phoenix Authorization Bypass Vulnerability

CVE-2026-82724 is an authorization bypass flaw in ash_phoenix that causes tenant-scoped access checks to fail by passing nil instead of the actual tenant. This post covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-82724 Overview

CVE-2026-82724 is an incorrect authorization vulnerability [CWE-863] in the ash-project/ash_phoenix library. The flaw resides in AshPhoenix.LiveView.SubdomainHook.on_mount/4, which invoked the handle_subdomain authorization callback with a nil tenant. Tenant-scoped access checks never observed the tenant they were meant to enforce, allowing cross-tenant access in multi-tenant Phoenix LiveView applications. The issue affects ash_phoenix from version 2.1.26 before 2.3.25.

Critical Impact

Consumer gates that halt navigation when a user does not belong to a tenant instead evaluated nil, either crashing or taking a permissive branch, enabling cross-tenant authorization bypass.

Affected Products

  • ash_phoenix version 2.1.26 and later
  • ash_phoenix versions prior to 2.3.25
  • Phoenix LiveView applications using AshPhoenix.LiveView.SubdomainHook for tenant scoping

Discovery Timeline

  • 2026-08-31 - CVE-2026-82724 published to NVD
  • 2026-09-01 - Last updated in NVD database

Technical Details for CVE-2026-82724

Vulnerability Analysis

The SubdomainHook.on_mount/4 function attached a handle_params hook to assign the tenant, then immediately called handle_subdomain within the same on_mount callback. LiveView writes the tenant assign only when it later runs handle_params, which executes strictly after on_mount returns. As a result, handle_subdomain read an unset assign and ran as apply(m, f, [socket, nil | a]).

A consumer gate that halts when the user does not belong to the tenant instead evaluated against nil. This produced one of two outcomes: the callback crashed, or it took a permissive branch that allowed the request through. The gate was never re-run once the real subdomain was assigned, nor on subsequent LiveView navigations.

Root Cause

The root cause is an ordering error between on_mount and handle_params lifecycle callbacks in Phoenix LiveView. The authorization callback executed before the tenant socket assign was populated, violating the assumption that handle_subdomain receives the resolved tenant.

Attack Vector

An authenticated attacker on the network can navigate to a LiveView route scoped to a tenant they do not belong to. Because handle_subdomain receives nil instead of the resolved subdomain, tenant membership checks fail open. The attacker gains access to data or actions in another tenant's context, breaching the confidentiality and integrity of tenant-isolated resources.

elixir
// Security patch in lib/ash_phoenix/live_view/subdomain_hook.ex
   def on_mount(opts, _params, _session, socket) when is_list(opts) do
     opts = Spark.Options.validate!(opts, @hook_options)
 
-    socket
-    |> assign_tenant(opts)
-    |> call_handle_subdomain(opts)
+    {:cont,
+     attach_hook(socket, :set_tenant, :handle_params, fn _params, url, socket ->
+       subdomain = AshPhoenix.Helpers.get_subdomain(socket, url)
+       socket = assign(socket, opts[:assign], subdomain)
+       call_handle_subdomain(socket, subdomain, opts)
+     end)}
   end
 
-  defp assign_tenant(socket, opts) do
-    attach_hook(socket, :set_tenant, :handle_params, fn
-      _params, url, socket ->
-        subdomain = AshPhoenix.Helpers.get_subdomain(socket, url)
-        {:cont, assign(socket, opts[:assign], subdomain)}
-    end)
-  end
-
-  defp call_handle_subdomain(socket, opts) do
+  defp call_handle_subdomain(socket, subdomain, opts) do
     case opts[:handle_subdomain] do
       {m, f, a} ->

Source: GitHub Commit b396e1a. The fix moves handle_subdomain inside the handle_params hook and passes the resolved subdomain explicitly, ensuring the callback runs with the real tenant on every navigation.

Detection Methods for CVE-2026-82724

Indicators of Compromise

  • Application logs showing handle_subdomain invocations where the tenant argument is nil or missing
  • LiveView process crashes originating from AshPhoenix.LiveView.SubdomainHook with FunctionClauseError or MatchError on tenant values
  • Access log entries where users successfully load LiveView routes across subdomains that should be tenant-restricted

Detection Strategies

  • Inventory dependencies for ash_phoenix versions between 2.1.26 and 2.3.24 inclusive using mix deps or SBOM tooling
  • Review custom handle_subdomain implementations for handling of nil tenants and add explicit halts on nil
  • Audit LiveView telemetry events for mount-time authorization decisions that resolved without a tenant present

Monitoring Recommendations

  • Instrument handle_subdomain callbacks with structured logging that records the tenant, user, and decision outcome
  • Emit metrics on cross-tenant navigation attempts and alert when the tenant field is empty in an authorization decision
  • Correlate Phoenix access logs with tenant assigns to detect sessions that mounted a LiveView without a resolved subdomain

How to Mitigate CVE-2026-82724

Immediate Actions Required

  • Upgrade ash_phoenix to version 2.3.25 or later in mix.exs and redeploy affected Phoenix applications
  • Audit handle_subdomain callbacks to reject nil tenants explicitly and halt the socket rather than allowing traversal
  • Review multi-tenant LiveView routes for prior cross-tenant access using authentication and application logs

Patch Information

The fix is committed in ash_phoenix commit b396e1a and released in version 2.3.25. See the GitHub Security Advisory GHSA-39c8-xcwr-gqff and the CNA CVE-2026-82724 Report for full disclosure details.

Workarounds

  • Add a defensive guard in application-level handle_subdomain that halts the socket when the tenant argument is nil
  • Enforce tenant checks inside handle_params directly rather than relying solely on on_mount hooks until the upgrade is applied
  • Restrict LiveView routes with a plug-based authorization layer that validates subdomain membership before the socket mounts
bash
# Update ash_phoenix in mix.exs
# {:ash_phoenix, "~> 2.3.25"}
mix deps.update ash_phoenix
mix deps.get
mix compile

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.