Most ColdFusion Examples Of EncodeForCSS() Are Broken
I absolutely love the encodeForXYZ()
functions in ColdFusion and Lucee CFML. They make life absolutely wonderful for developers; and, more importantly, they make life safe for users because they prevent hosted and reflected XSS (Cross-Site Scripting) attacks. But, the one encoding function that sticks out like a sore-thumb is encodeForCss()
. The overwhelming majority of examples that exist for this function tend to use color as the demonstration context. However, this is broken! encodeForCss()
- at least in ColdFusion - does not work for color. So, where the heck can you use encodeForCss()
in ColdFusion? I wanted to explore this question in Lucee CFML 5.3.6.61.
First, let's just dispense with the idea that we can use encodeForCss()
for any kind of color, whether it be color
or background-color
:
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
p {
padding: 10px ;
}
</style>
</head>
<body>
<cfoutput>
<cfset color1 = "red" />
<cfset color2 = "##ff0000" />
<cfset color3 = "rgb( 255, 0, 0 )" />
<!--- First, let's try a control group with un-encoded values. --->
<p style="background-color: #color1# ;"> Color 1 </p>
<p style="background-color: #color2# ;"> Color 2 </p>
<p style="background-color: #color3# ;"> Color 3 </p>
<hr />
<!---
Now, let's try an encoded group for "color", which is basically like EVERY
DEMO I'VE EVER SEEN with regard to how we can use encodeForCss().
--
CAUTION: THIS DOES NOT WORK.
--->
<p style="background-color: #encodeForCss( color1 )# ;"> Color 1 </p>
<p style="background-color: #encodeForCss( color2 )# ;"> Color 2 </p>
<p style="background-color: #encodeForCss( color3 )# ;"> Color 3 </p>
</cfoutput>
</body>
</html>
Here, we're trying three different valid color syntaxes. The major downfall of almost every encodeForCss()
demo is that it uses the first color syntax: HTML color codes like red
and pink
and gold
. encodeForCss()
does work for that type of color - but, fails for everything else:
As you can see, color
or background-color
choices like red
and gold
will work fine - which is perhaps why people haven't noticed that most of the encodeForCss()
examples are fundamentally broken. But, the moment you try to use any of the more common syntax choices, encodeForCss()
stops working.
Now, I know that the encoder methods are based on the OWASP (Open Web Application Security Project) encoder recommendations; so, I tried to dig deeper and look at how the OWASP encoder works. Which is when I found this description:
public static String forCssString(String input)
Encodes for CSS strings. The context must be surrounded by quotation characters. It is safe for use in both style blocks and attributes in HTML.
Note the phrase, "The context must be surrounded by quotation characters". As it turns out, most CSS property values do not work if quoted. So, it seems that encodeForCss()
won't work for most CSS property values. But, some CSS properties can be quoted; so, let's try with a few of those.
The first one that jumps to mind is background-image
. You don't technically need to quote the URL within a background-image
; but, I always do because I think it looks nicer:
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
p {
background-repeat: no-repeat ;
background-size: cover ;
height: 150px ;
width: 300px ;
}
</style>
</head>
<body>
<cfoutput>
<cfset backgroundUrl = "https://bennadel-cdn.com/images/header/photos/jeremiah_lee_2.jpg" />
<!--- First, let's try a control group with un-encoded values. --->
<p style="background-image: url( #backgroundUrl# ) ;">
Test
</p>
<hr />
<!---
Now, let's try an encoded group for background-image. This seems to be the
sweet-spot for encodeForCss() - URLs.
--->
<p style="background-image: url( #encodeForCss( backgroundUrl )# ) ;">
Test
</p>
<!--- With a quoted value. --->
<p style="background-image: url( '#encodeForCss( backgroundUrl )#' ) ;">
Test
</p>
</cfoutput>
</body>
</html>
Again, we have a "control group" with un-encoded value. And then, subsequent attempts to use encodeForCss()
with both quoted and unquoted URLs. And, when we run this in the browser, we get the following output:
As you can see, encodeForCss()
does work with the quoted CSS property, background-image
.
Another commonly quoted value is content
:
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
p {
padding: 10px ;
}
</style>
</head>
<body>
<cfoutput>
<cfset content = "That's <em>cool</em>!" />
<!--- First, let's try a control group with un-encoded values. --->
<style type="text/css">
.p1:before {
content: "#content#" ;
}
</style>
<p class="p1"></p>
<hr />
<!---
Now, let's try an encoded group for content. Encoding seems to work quite
nicely for any value that is surrounded by quotes.
--->
<style type="text/css">
.p2:before {
content: "#encodeForCss( content )#" ;
}
</style>
<p class="p2"></p>
</cfoutput>
</body>
</html>
Again, we have an un-encoded control group and then an encoded test. And, when we run this in the browser, we get the following output:
As you can see, the encodeForCss()
function works with the traditionally-quoted CSS property, content
.
One more commonly-quoted property is font-family
. So, let's try that:
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
p {
padding: 10px ;
}
</style>
</head>
<body>
<cfoutput>
<cfset fontFamily = "source code pro" />
<!--- First, let's try a control group with un-encoded values. --->
<p style="font-family: #fontFamily# ;"> Font Family </p>
<hr />
<!---
Now, let's try an encoded group for content. Encoding seems to work quite
nicely for any value that is surrounded by quotes.
--->
<p style="font-family: #encodeForCss( fontFamily )# ;"> Font Family </p>
<!--- With a quoted value. --->
<p style="font-family: '#encodeForCss( fontFamily )#' ;"> Font Family </p>
</cfoutput>
</body>
</html>
And, when we run this in the browser, we get the following output:
As you can see, encodeForCss()
works perfectly well with the commonly-quoted CSS property, font-family
.
The biggest problem with encodeForCss()
is that it works by coincidence sometimes. Which is, I suspect, why the vast majority of examples of encodeForCss()
in the ColdFusion world are broken. For example, this works:
width: #encodeForCss( '50px' )# ;
... but, this is broken:
width: #encodeForCss( '50%' )# ;
And, this works:
border-radius: #encodeForCss( '10px' )# ;
... but, this is broken:
border-radius: #encodeForCss( '10px 20px' )# ;
So, my take-away here is that the encodeForCss()
in ColdFusion and Lucee CFML is only safe to be used with CSS property values that are quoted. And, isn't meant for anything else, despite the fact that it sometimes works in other situations.
Ultimately, the problem with this is that if the encodeForCss()
function is intended to prevent malicious attacks embedded within user-provided content, it can't really do its job if it can't be used in all CSS contexts. Which is unfortunate. Especially since color is one of the primary use-cases for user-provided content being consumed in CSS.
Want to use code from this post? Check out the license.
Reader Comments