EEF-CVE-2026-117645: Phoenix.HTML vulnerable to attribute injection via unvalidated attribute names in attributes_escape/1

Published Oct 30, 2026
·
Updated

Summary

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') vulnerability in phoenixframework phoenixhtml allows an attacker who controls attribute names to inject attributes and script into rendered HTML via Phoenix.HTML.attributesescape/1.

attributesescape/1 in lib/phoenixhtml.ex escapes attribute values but not attribute names. keyescape/1 passes a binary key through htmlescape, which neutralizes quotes and angle brackets but not whitespace, so a key such as x onmouseover ends the attribute and starts a new one. An atom key is only dasherized, so any character passes, including quotes and >. Nested data, aria and phx keys take the same paths. An application is affected when it builds attribute keys from untrusted input and renders them through HEEx <tag {@attrs}>, contenttag/3, or a direct call. Before 3.0.0 the same code lived in Phoenix.HTML.Tag.

This issue affects phoenixhtml: from 1.0.0 before 3.3.5 and from 4.0.0 before 4.4.0.

Details

1. Attribute rendering. Phoenix.HTML.attributesescape/1 walks the key-value pairs through buildattrs/1 and nestedattrs/3 and emits each as keyescape(k)="attrescape(v)". Values are escaped. Names are not checked against the HTML attribute-name grammar.

2. Binary names. keyescape/1 sends a binary through Phoenix.HTML.Engine.htmlescape/1, which replaces &, <, >, " and '. Whitespace passes. HTML uses whitespace to separate attributes, so %{"x onmouseover" => "alert(1)"} renders x onmouseover="alert(1)": a harmless x attribute followed by an attacker-chosen event handler.

3. Atom names. keyescape/1 converts an atom with Atom.tostring/1 and replaces with -. Nothing is escaped, so [{:"x\" onclick=\"alert(1)", "y"}] renders x" onclick="alert(1)="y" and aria: [{:"x\"><script>", "y"}] renders aria-x"><script>="y", which leaves the tag entirely. This path needs the application to create atoms from untrusted input, for example Jason.decode!(body, keys: :atoms) or String.toatom/1 on a parameter.

4. Reachability. HEEx compiles <div {@attrs}> to a call to Phoenix.HTML.attributesescape/1 through Phoenix.LiveView.HTMLEngine and Phoenix.LiveView.TagEngine. tag/2 and contenttag/3 in Phoenix.HTML.Tag (phoenixhtml 1.x to 3.x) and in PhoenixHTMLHelpers.Tag (4.x) render attributes the same way. Versions before 3.0.0 emitted the dasherized name without any escaping at all.

Proof of concept

1. Install a vulnerable release, for example Mix.install([{:phoenixhtml, "== 4.3.0"}]). 2. Render a binary name containing a space: Phoenix.HTML.attributesescape(%{"x onmouseover" => "alert(1)"}) |> Phoenix.HTML.safetostring() returns x onmouseover="alert(1)". 3. Render an atom name containing a quote: Phoenix.HTML.attributesescape([{:"x\" onclick=\"alert(1)", "y"}]) |> Phoenix.HTML.safetostring() returns x" onclick="alert(1)="y". 4. Render a nested atom name: Phoenix.HTML.attributesescape(aria: [{:"x\"><script>", "y"}]) |> Phoenix.HTML.safetostring() returns aria-x"><script>="y". 5. Place any of these maps in a HEEx template as <div {@attrs}> to deliver the injected attribute to a browser.

Impact

An attacker who can influence the attribute names an application renders can add event-handler attributes or close the tag and run script in the browser of every user who views the page.

Workarounds

Do not derive attribute names from untrusted input. Where dynamic attribute keys are needed, check each key against an allow list, or against a pattern such as ^[a-zA-Z][a-zA-Z0-9:.-]$, before passing the map to attributesescape/1 or a HEEx {@attrs} expression. Do not create atoms from request data.

Other sources

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') vulnerability in phoenixframework phoenixhtml allows an attacker who controls attribute names to inject attributes and script into rendered HTML via Phoenix.HTML.attributesescape/1.

attributesescape/1 in lib/phoenixhtml.ex escapes attribute values but not attribute names. keyescape/1 passes a binary key through htmlescape, which neutralizes quotes and angle brackets but not whitespace, so a key such as x onmouseover ends the attribute and starts a new one. An atom key is only dasherized, so any character passes, including quotes and >. Nested data, aria and phx keys take the same paths. An application is affected when it builds attribute keys from untrusted input and renders them through HEEx <tag {@attrs}>, contenttag/3, or a direct call. Before 3.0.0 the same code lived in Phoenix.HTML.Tag.

This issue affects phoenixhtml: from 1.0.0 before 3.3.5 and from 4.0.0 before 4.4.0.

— OSV

Affected Software

2 affected componentsFixes available
erlang/phoenix_html>=1.0.0<3.3.5
3.3.5
erlang/phoenix_html>=4.0.0<4.4.0
4.4.0

Event History

Oct 30, 2026
CVE Published
via OSV·08:25 PM
Data Sourced
via OSV·08:25 PM
DescriptionSeverityWeakness
Data Sourced
via OSV·08:25 PM
Affected Software

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203