CVE-2026-56812 Overview
CVE-2026-56812 is a client-side denial of service vulnerability in the Phoenix Framework Presence JavaScript client. The flaw resides in assets/js/phoenix/presence.js, specifically in the Presence.syncState and Presence.syncDiff routines. An attacker with ordinary channel access can join a Phoenix channel using a key that collides with an Object.prototype member name such as __proto__, constructor, or toString. The resulting uncaught TypeError breaks presence synchronization for every viewer of the affected channel topic until the attacker leaves. The issue is classified under [CWE-754] (Improper Check for Unusual or Exceptional Conditions).
Critical Impact
A single low-privileged user can persistently disable presence sync for all other viewers of a channel topic by choosing a username or ID that matches a JavaScript prototype member name.
Affected Products
- Phoenix Framework 1.2.0-rc.0 through versions before 1.5.15
- Phoenix Framework 1.6.0-rc.0 through versions before 1.6.17
- Phoenix Framework 1.7.0-rc.0 through versions before 1.7.24
- Phoenix Framework 1.8.0-rc.0 through versions before 1.8.9
Discovery Timeline
- 2026-07-07 - CVE-2026-56812 published to NVD
- 2026-07-09 - Last updated in NVD database
Technical Details for CVE-2026-56812
Vulnerability Analysis
The Phoenix JavaScript presence client tracks presences in a plain JavaScript object keyed by attacker-controlled identifiers such as usernames or IDs. The client checks whether a presence already exists using a bare truthiness test (state[key]) instead of an own-property check like Object.prototype.hasOwnProperty.call(state, key). When a user joins a channel with a key that matches an inherited Object.prototype member such as __proto__, constructor, toString, or hasOwnProperty, the lookup returns the built-in prototype method instead of undefined. Because the prototype value is truthy, the code treats it as an existing presence entry and attempts to read .metas.map(...) on it, throwing an uncaught TypeError.
Root Cause
Both syncState and syncDiff use the same unsafe existence-check pattern. The exception propagates out of the presence message handler, preventing the local state from being updated and stopping onSync() from firing. Because the malicious key is tracked on the server, it is re-pushed on every presence update, so the exception recurs indefinitely for every viewer of that channel topic. This is a read-time confusion of the prototype object, not a mutation of Object.prototype, so it is not a prototype pollution vulnerability.
Attack Vector
An attacker joins a Phoenix channel using a presence key that collides with an Object.prototype member name. No elevated privileges are required beyond the ability to join the channel. The presence sync remains broken for every other viewer of that topic until the attacker disconnects. The vulnerability is network-reachable but affects only client-side JavaScript state.
// Security patch from Phoenix Framework
// Before (vulnerable):
// this.state = {}
// After (patched):
constructor(channel, opts = {}){
let events = opts.events || {state: "presence_state", diff: "presence_diff"}
- this.state = {}
+ this.state = Object.create(null)
this.pendingDiffs = []
this.channel = channel
this.joinRef = null
The fix replaces the plain object literal with Object.create(null), producing a prototype-less object where inherited member lookups return undefined. Source: Phoenix commit 89a1c4be.
Detection Methods for CVE-2026-56812
Indicators of Compromise
- Client-side JavaScript console errors reporting TypeError from presence.js referencing .metas.map on undefined or prototype values.
- Repeated failure of onSync() callbacks across multiple browser sessions viewing the same channel topic.
- Server-side presence tracking entries where the tracked key matches an Object.prototype member name such as __proto__, constructor, toString, or hasOwnProperty.
Detection Strategies
- Inspect Phoenix Presence.track/3 call sites and log any presence keys that match reserved JavaScript prototype names before publishing to channels.
- Deploy a temporary allowlist or regex validator on server-side presence keys and alert when a rejected key matches a prototype member name.
- Correlate frontend error telemetry (e.g., Sentry, Datadog RUM) for TypeError originating in phoenix/presence.js with server-side channel join events.
Monitoring Recommendations
- Ingest browser JavaScript error events into a centralized logging pipeline and build an alert for stack traces referencing syncState or syncDiff from Phoenix.
- Track per-topic counts of unique presence keys and flag topics containing keys that match Object.prototype member names.
- Monitor for sustained drops in successful onSync() invocations relative to presence_diff message volume, which suggests handler exceptions.
How to Mitigate CVE-2026-56812
Immediate Actions Required
- Upgrade Phoenix to 1.5.15, 1.6.17, 1.7.24, or 1.8.9 or later depending on the branch in use.
- Rebuild and redeploy frontend bundles that include phoenix.js so patched client code is served to browsers.
- Add server-side validation that rejects presence keys matching Object.prototype member names until the client patch is deployed.
Patch Information
The Phoenix Framework maintainers released fixes across four branches. The core change replaces this.state = {} with this.state = Object.create(null) in the Presence constructor, ensuring prototype chain lookups no longer occur during presence existence checks. Review the GitHub Security Advisory GHSA-63mc-hw7g-86rr and the CNA advisory from erlef.org for full details.
Workarounds
- Sanitize presence keys on the server before calling Presence.track/3, rejecting or transforming values that match reserved prototype member names.
- Wrap syncState and syncDiff invocations in a defensive try/catch on the client to prevent handler exceptions from stopping presence updates.
- Namespace presence keys with a fixed prefix (for example, user:<id>) so attacker-controlled input cannot collide with Object.prototype members.
# Update Phoenix in mix.exs to a patched release, then fetch dependencies
# and rebuild assets so the patched JavaScript client is served.
mix deps.update phoenix
mix deps.get
mix assets.deploy
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

