Skip to main content
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Andrew Duvall
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with: Andrew Duvall

Preventing HTMX And Alpine.js From Binding To User Generated Content

By
Published in ,

On this blog, I try to keep things securely locked down. On the ColdFusion side, I run all user generated content (UGC) through a JSoup sanitizer (though I previously used the OWASP AntiSamy project); and now that I've switched my client side libraries from Hotwire (Turbo and Stimulus) over to HTMX 4 and Alpine.js, I want to make double sure that nothing in the UGC output accidentally triggers HTMX or Alpine.js behaviors.

I say "double sure" because my JSoup cleaner already works with a whitelist of tags and attributes that implicitly block all hx-* (HTMX) and x-* (Alpine) bindings. So, this is more about completeness than it is about any known security threat.

Both HTMX and Alpine.js work by walking the DOM (Document Object Model) tree and attaching JavaScript functionality to declarative attributes. And, both libraries provide a mechanism for excluding a given DOM branch from this traversal:

  • HTMX 4 - uses hx-ignore to exclude DOM branches.
  • Alpine.js - uses x-ignore to exclude DOM branches.

As such, all I have to do in order to prevent malicious HTMX / Alpine.js invocation is add these attributes wherever I output my user generated content. For example, here's a truncated look at how I now output the list of comments on my blog post detail view:

<cfoutput>
	<cfloop array="#comments#" index="comment">

		<article class="comment">
			<header class="meta">
				<span class="author">
					#e( comment.member.name )#
				</span>
				<span class="date">
					#dateFormat( comment.createdAt, "mmm d, yyyy" )# at
					#timeFormat( comment.createdAt, "h:mm TT" )#
				</span>
			</header>

			<!---
				USER GENERATED CONTENT (UGC): this has already been run through
				the JSoup sanitizer, so we know it's safe to output. But, as a
				hardening measure, prevent HTMX/Alpine bindings as well!
			--->
			<div hx-ignore x-ignore class="content">
				#comment.content#
			</div>
		</article>

	</cfloop>
</cfoutput>

As you can see, the <div> that wraps each user's individual comment has both hx-ignore and x-ignore attributes. This eliminates any chance that a malicious attribute can trigger HTMX or Alpine.js behaviors.

Again, this is basically unnecessary thanks to my JSoup cleaner. But, it safe-guards against any mistakes that I might make in the future; and, it's just a night thought experiment that leads me to a more correct application / page state.

Want to use code from this post? Check out the license.

Reader Comments

Post A Comment — I'd Love To Hear From You!

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
— Ben Nadel
Managed ColdFusion hosting services provided by:
xByte Cloud Logo