Enhancing ColdFusion Script Protection - Security Series #10

NOTE: This is stupid. Script Protect sucks. Encode your output properly and consider a real web application firewall, not this stupid, ineffective, black-list crap.

So anyone that has ever turned on Script Protection has either been annoyed by something that it does, or by something that it doesn't do. For me, it was both. I recently spoke with someone at bFlex about it, and he was having some of the same issues I had had with ColdFusion Script Protection. So, I finally decided to dig into it and see what could be done.

The Problem

There are two problems with ColdFusion Script Protection:

  1. It covers too much
  2. It doesn't cover enough

Well, that weird, isn't it? Yes, it's true. Script Protection is too strong and too weak at the same time.

Too Strong

"Too strong" might no be exactly the right way to describe this behavior in script protection, but this behavior bugs me none-the-less.

Let's say that someone tries to inject some Cross-Site Scripting code into the comments section of your blog. Something like:


<script type="text/javascript>

alert("pown'd");
</script>

Well ColdFusion Script Protection would turn that little diddy into this:


<InvalidTag type="text/javascript>
alert("
pown'd");
</script>

That's exactly what we want. This part of script protection is great. It would do the same thing with the Object, Embed, Applet, and Meta tags.

Now, let's pretend that someone (maybe even me) wants to put something on my blog about Transfer-ORM configuration files. Which look like this:


<?xml version="1.0" encoding="UTF-8"?>
<objectDefinitions>

    <package name="pages">
        <InvalidTag name="page" table="pages">
            <id name="pageid" type="UUID" />
            <property name="pagename" type="string" nullable="false" />
            <property name="pagetitle" type="string" nullable="true" />
            ...
            
            <onetomany name="section" lazy="true">
                <link to="pages.section" column="fkpageid"/>
                <collection type="array">
                    <order property="sortno" order="asc"/>
                </collection>
            </onetomany>
        </object>
        
        ...
</objectDefinitions>
</transfer>

Well, you see that <objectDefinitions> tag? It has the pattern "<object" in it. That means Script Protection is gonna see it as a threat.

So my Transfer config example is going to look like this.


<?xml version="1.0" encoding="UTF-8"?>
<InvalidTagDefinitions>
...
</objectDefinitions>

That sucks. And since the <objectDefinitions> is harmless when displayed, there is no reason for it to get filter like this.

This is the same issue that I heard about at bFlex, though a different tag was being filtered.

Too Weak

ColdFusion Script Protection is also too weak. It does not catch enough troublesome scripts. One of the biggies that it doesn't get, and I really can't understand why, is <iframe>.

IFrames are incredibly dangerous and can be used for some pretty nefarious stuff. Granted, if you are properly escaping your user-generated output with HTMLEditFormat() this shouldn't be a concern, but it is a dangerous assumption to believe we are perfect. There may be some other tags or patterns we would like to see removed as well.

The Solution

So, it turns out that changing this behavior in ColdFusion Script Protection is pretty easy. There is an XML config file that contains patterns that are compared to the incoming data. If a match is made, then the replacement value is inserted. The patterns are simple Regular Expressions (RegEx).

So not being an expert in Regular Expressions, I asked Ben Nadel to help me figure this out. He helped me find the RegEx solution I needed very quickly. Thanks Ben!

In your ColdFusion installation you will find a /lib folder.

Windows users: C:\ColdFusion8\lib Mac Users: /Applications/ColdFusion/lib *nix: /opt/coldfusion/lib (I think)

In the /lib folder you will find a file called neo-security.xml. Near the bottom of this file you'll see something that looks like:


    <var name='CrossSiteScriptPatterns'>
        <struct type='coldfusion.server.ConfigMap'>
            <var name='<\s*(object|embed|script|applet|meta)'>
                <string>
                    <InvalidTag
                </string>
            </var>
        </struct>
    </var>

What this is saying is, if you come across something that matches the RegEx Pattern "&lt;\s*(object|embed|script|applet|meta)", then replace those characters with &lt;InvalidTag.

Now, normally I would think that this is fine, but like I said, I do not want <objectDefinition to be turned into <InvalidTagDefinition.

So, how do I stop this? Well first, I take the "object" text out of the existing patterns and make a new pattern with it. And I add a little extra.


    <var name='CrossSiteScriptPatterns'>
        <struct type='coldfusion.server.ConfigMap'>
            <var name='<\s*(embed|script|applet|meta)'>
                <string>
                    <InvalidTag
                </string>
            </var>
            <var name='<\s*object(?!Definitions)'>
                <string>
                    <InvalidTag
                </string>
            </var>
        </struct>
    </var>

So here, I have added another pattern for the XSS filter to try to match. This patterns says to match <object unless it is <objectDefinitions.

This could also be expanded to include additional patterns that begin with "<object".


    <var name='CrossSiteScriptPatterns'>
        <struct type='coldfusion.server.ConfigMap'>
            <var name='<\s*(embed|script|applet|meta)'>
                <string>
                    <InvalidTag
                </string>
            </var>
            <var name='<\s*object(?!(Definitions|ive))'>
                <string>
                    <InvalidTag
                </string>
            </var>
        </struct>
    </var>

Here, it will not match "<objectDefinitions" or "<objective".

Very nice.

Now to discuss adding the <iframe> tag. This is actually even easier.


    <var name='CrossSiteScriptPatterns'>
        <struct type='coldfusion.server.ConfigMap'>
            <var name='<\s*(embed|script|applet|meta)'>
                <string>
                    <InvalidTag
                </string>
            </var>
            <var name='<\s*object(?!(Definitions|ive))'>
                <string>
                    <InvalidTag
                </string>
            </var>
            <var name='<\s*iframe'>
                <string>
                    <InvalidTag
                </string>
            </var>
        </struct>
    </var>

Simply by adding another <var> to the <struct> I can add this pattern "&lt;\s*iframe" which will find <iframe.

UPDATE: I forgot to mention in this post, after you change the neo-security.xml that you must restart the ColdFusion Application Server for the changes to take effect.

Comments
Peter Boughton's Gravatar What about other things, like <InvalidTagion>?

Rather than have a list of exceptions, it's simpler to only match where object (and all the other keywords) are the whole word - that is, they are terminated by a word boundary.

i.e.
<\s*(embed|script|applet|meta|object|iframe)\b

That still prevents <InvalidTag..> and so on, but gives more flexibility for safe stuff.
# Posted By Peter Boughton | 9/10/08 12:55 AM
Peter Boughton's Gravatar Heh...
# Posted By Peter Boughton | 9/10/08 12:57 AM
Azadi's Gravatar in light of recent sql injection attacks, i wonder if the scriptprotect settings can be extended even further to protect against those kind of things...
# Posted By Azadi | 9/10/08 4:54 AM
Jason Dean's Gravatar @Peter,

I agree that it would be simpler to only block &lt;object rather than to block everything and add a list of exceptions. However, when it comes to blocking HTML and javascript, I believe erring on the side of caution is the right way to go. Also, hackers are a clever lot. They might figure out how to get past the block for &lt;object with something like:

&lt;script%20type="text/javascript">
or
&lt;script%00%20teype="">

So in this case, I think the "Default Deny" mentality where we keep it closed and make exceptions is better than "Default Allow" where we open it up and add things to exclude.
# Posted By Jason Dean | 9/10/08 8:35 AM
Jason Dean's Gravatar @Azadi

That is a thought, however, there are SO many different types of attacks that could be used. I think it might be inefficient to use Script Protect.

It might be worth looking into an running some benchmarks on, but I suspect this is a job for something like a .htaccess file. As Luis Majano describes here: http://tinyurl.com/5cuzw5
# Posted By Jason Dean | 9/10/08 8:43 AM
Peter Boughton's Gravatar Whilst erring on the side of caution is sensible, the regex word boundary character will match /anything/ that is not a word character - including the null - so it is fine.

Of course, erring on the side of caution, you should be escaping ALL tags, and then whitelisting acceptable ones, rather than blacklisting the six known/current risks.
# Posted By Peter Boughton | 9/10/08 8:44 AM
Jason Dean's Gravatar @Peter, Thanks for the explanation of the boundary character. Good to know. I did not know it would catch the URL encoded Null or White-space. I will need to experiment with that.

Perhaps you are right and blocking only those tags is the better solution. I am trying not to deviate too much from the model that Adobe/Macromedia already had set up. Instead of redoing it completely, I am adding exceptions to it. To me, it seems the safer course. It is certainly possible Adobe/Macromedia knows something about XSS that I don't. Perhaps there are hacks that are possible even without using Null-characters or White-space characters.

Or possibly they just wrote bad regex to start with an never got around to fixing it.
# Posted By Jason Dean | 9/10/08 8:59 AM
Rachel Lehman's Gravatar It would be nice if ScriptProtect was configurable and extendable... as hackers come up with new ways to do XSS and SQL injection, having a standard way to prevent them would be convenient. I'm imagining that if you could block particular tags (iframe)or even provide your own regular expressions it would be powerful. Maybe making these features available in an "advanced mode" only so that every day users who like the feature set as-is don't have to mess with the config.
# Posted By Rachel Lehman | 9/10/08 9:02 AM
Jason Dean's Gravatar @Rachel

That sure would be nice. I may have to submit that feature request. Thanks for the idea.

Also, thanks, you reminded me that I need to update my post. I forgot to mention that after you change the neo-security.xml that you must restart the ColdFusion Application Server.
# Posted By Jason Dean | 9/10/08 9:07 AM
Sebastiaan's Gravatar How would this security-story relate to Railo Barry (3.2.1)? I know it does the sme thing, 'cause injecting script-tags leads to it displaying InvalidTag. But as with many things with Railo this isn't really well documented or tested/blogged about. Do you have any experience with this? Can I assume all your Security-series also relate to /are usable for Railo?
# Posted By Sebastiaan | 3/5/10 3:35 AM
Jason Dean's Gravatar @sebastian

I tried out scriptProtect in Railo (Barry) and in the few minutes I played with it seems to work similarly to ColdFusion's script Protect, except that it DOES block <iframe> tags. I did not test extensively enough to say otherwise.

I could not find a way to customize the scriptProtect feature of Railo, which is disappointing. If the code is buried in a jar somewhere, I would hate to have to recompile just to improve that support.

As for assumption, many of my security posts apply to Railo and OpenBD just as they would apply to PHP or Java. Most application security topics span all languages. But as for specific server features, like script protect or session management for example, do not assume that they work the same way in Railo or OpenBD. If I test either of the other engines, I will say so, but typically I work with Adobe ColdFusion, so that is my primary test bed.

Also, take a look at my recent post about scriptProtect which, likely, applies to Railo and OpenBD as well.

http://www.12robots.com/index.cfm/2010/3/1/A-warni...
# Posted By Jason Dean | 3/5/10 12:45 PM
Sebastiaan's Gravatar Hi Jason,

I've read you Security series and "I liked it a lot" ;-)

Have made the switch to Railo last year and am developing for that CF-engine at the moment. It's free, it's wicked fast and it has those really nice administrators that I can use per client and one for the whole server. Furthermore it does most things CF8 does, and all the fuss about CF9 being so incredibly fast compared to CF8 - looking at the testdata I have my doubts (they use two different JRE's in the test, hopefully it's a typo but Adobe wasn't sure...).

As for the recent article, that was how I came upon this article ;-)
# Posted By Sebastiaan | 3/6/10 11:10 AM
Evgen Evgen's Gravatar Have made the switch to Railo last year and am developing for that CF-engine at the moment. It's free, it's wicked fast and it has those really nice administrators that I can use per client and one for the whole server. Furthermore it does most things CF8 does, and all the fuss about CF9 being so incredibly fast compared to CF8 - looking at the testdata I have my doubts (they use two different JRE's in the test, hopefully it's a typo but Adobe wasn't sure...).
http://evgenwatch.com/
# Posted By Evgen Evgen | 2/27/12 8:00 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner