Portcullis - Protect against SQL Injection and XSS

Apr 30, 2008

I recently released a public version of a CFC filter called Portcullis that can help protect your applications from SQL Injection and Cross site scripting attacks. It attempts to cover the areas that ColdFusion's script protect feature does not cover adequately.

http://portcullis.riaforge.org

SQL Injection and XSS attacks have been the attacks du jour for hackers. They have also been in the OWASP's Top 10 most serious web application vulnerabilities for several years now (http://www.owasp.org/index.php/Top_10_2007). In fact, XSS (cross-site scripting) and injection attacks are ranked first and second respectively on the list.

Now since ColdFusion 7, we have had a functionality called "scriptprotect" that you can set in the cfapplication tag or in the application.cfc. There are several problems with this feature and to be honest I wish that Adobe rework their logic or make it very clear in the CFAdmin and documentation that this does not do very much. I see too many people turning this feature on and thinking their work is done.

If you do use ColdFusion's script protection, keep in mind a few things. Saying scriptprotect=true does NOT properly turn on script protection. It also does NOT error out, which it should due to the nature of this attribute. The setting should instead be a list of scopes you wish to protect. For example, scriptprotect="form,url,cookie" or scriptprotect="all". Secondly, the script protect feature is more geared towards XSS attacks. The thought being, I guess, that cfqueryparam and cfstoredproc will cover all the SQL injections? This is also a very bad assumption.

Unfortunately, their list of tags to strip is very easy for a hacker to walk around. To view and modify the settings for this, simply go to your neo-security.xml in the CFInstallDirectory\lib\..

At the bottom you will see..

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

So ColdFusion's scriptprotect looks for tags in this list and replaces the bad tag with InvalidTag. Again this list is not very good. You should also include at the very least  "frame,iframe,frameset,layer,ilayer,form,input" to this list.

Another bad aspect to ColdFusion's script protect feature is that you have no log of the attacks or a methodology of stopping the attacker. There should be a method of logging these attacks and also to block the attacker's future requests. Hopefully, these items could appear in the next release of ColdFusion :)

Portcullis attempts to fill in these problems. With a more robust list of tags, keywords and SQL commands to filter out. It also logs the attempts and can temporarily block an IP that is sending the attacks.

Portcullis will work for ColdFusion versions 6 to 8. If you are using an older version of ColdFusion, I highly encourage you to use Shawn Gorrell's cf_xssblock tag. To be completely honest, Shawn's tag inspired Portcullis and he hits many of the same areas. You can find it at, http://www.illumineti.com/documents/xssblock.txt

Important Note:
If you are dealing with Ecommerce applications, bare in mind that PCI-DSS's requirement 6.6 is coming due on June 30th. You must have either a complete code review by a third-party that specializes in application security or have a Web Application Firewall (WAF) in place to protect your web-facing applications. Both are good, but in light of SQL Injection and XSS, having an application layer firewall provides another system to help filter and block these attacks and is highly encourage. FusionLink does provide WAF protection with their hosting.

Comments

RebeccaST

RebeccaST wrote on 06/02/08 6:29 PM

Hi, can you explain a little further how Portcullis should work?

You said:
<cfset application.Portcullis.scan(url,"url",cgi.remote_addr)>
<cfset application.Portcullis.scan(form,"form",cgi.remote_addr)>
<cfset application.Portcullis.scan(cookie,"cookie",cgi.remote_addr)>

Do you have to put an explict URL in the "url"? Same with "form"? What goes in there? Can you do it globally? Ie. all urls...?

Thanks for any help.
Rebecca_ST
John Mason

John Mason wrote on 06/04/08 12:54 AM

The "url" is simlpy the scope you wish the information to be thrown back into. By default, it will scan all the variables in those scopes that you scan.
Marc

Marc wrote on 07/22/08 8:07 AM

I tried to add this to some of my sites, but noticed that traffic being referred to the sites from search engines (google, yahoo, etc) was being intercepted as "bad" traffic and it was blocked. Is this intended behavior or is there something I have misconfigured?
John Mason

John Mason wrote on 07/22/08 8:18 AM

@Marc

I'll email you directly. It really depends on how you configure things.
Chris Giaccone

Chris Giaccone wrote on 07/22/08 12:07 PM

Please email me as well. Any additional directions would be very helpful. I attempted to customize using the basic settings in the portcullis.cfc and they don't seem to be doing anything. Real world examples of proper entries for those settings would be great.
John Mason

John Mason wrote on 07/22/08 3:11 PM

@Chris..

I don't have your email. Feel free to email me at mason at fusionlink.com
James Moberg

James Moberg wrote on 08/06/08 3:59 PM

I found an issue. When scanning the cookie scope and using "domain" cookies, Portcullis incorrectly rewrites the cookies as "host" cookies. I was setting domain=".domain.com" and then watching as "www.domain.com"; cookies were being auto-generated by Portcullis with a "/" path and then another cookie created with a sub-directory path. When logging out, I'm deleting the domain cookie, but the unwanted host cookies linger and continue to work. Is there any way to fix this and determine cookie type and retain it? Thanks.
JoMo

JoMo wrote on 08/25/08 5:48 PM

Recently, a site we manage was hit with SQL injections, and Portcullis has done a great job stopping them.

However, this website has a form that uses htmleditformat to post blocks of HTML to the database. It seems that Portcullis is escaping characters (i.e. semi-colons) that are already escaped with htmleditformat.

E.g. < turns into <; So, I get an extra semi-colon...

If I toggled variables.instance.escapeChars to false, would it solve this problem? Would that seriously weaken the script?
John Mason

John Mason wrote on 08/25/08 8:38 PM

@JoMo, you have simply have Portcullis handle the htmleditformat. That way you wouldn't have it effectively running twice over and doing that.

@James, thanks for that note on the cookies. I'll have a fix for that in the next version.
John Mason

John Mason wrote on 08/25/08 9:14 PM

Correction, JoMo's initial comment was actually the easier option for this situation. Just turn the escapechars to false. The htmleditformat() is taking care of that job. It's important though to note that xmlformat() is actually slightly better. It escapes more stuff. Then the escapechars function in portcullis covers even more. Something to consider when refactoring things.
Catherine Mortali

Catherine Mortali wrote on 08/26/08 8:52 AM

John,
I've installed this on a couple of my smaller sites this morning to test. I'm also finding (as Marc) that search engine referrals as well as some other valid site links are causing an error/block.

I haven't made any modifications to the portcullis.cfc tag and thinking that I should. Still seeing a TON of cast attacks which is what I'm trying to avoid seeing!

Thanks.
John Mason

John Mason wrote on 08/26/08 11:45 AM

@Catherine, the default configuration for portcullis should be blocking the cast attacks. They will still try to hit the site though. They tend to roll through in waves. Unless you block ip addresses, you can't really block the attempts.

Portcullis can break some functionality of a site because it's looking for certain words to strip out. If you have reserved sql command words in your url like 'method=insert', then I would advise modifying your code a bit to do 'method=add' instead. You should avoid using key sql command words in any url statements. Feel me to email if you need any help with this at mason |at| fusionlink.com
mark fennell

mark fennell wrote on 05/07/09 12:57 PM

Hi John,
This is a great piece of work. Not that I expected less from you! Anyhoo, I've run into a situation where "Walter" in a form field value is converted to "W[INVALID]". I thought it was funny, but the users didn't find it amusing. I took alter out of the sqlFilter list, but I was wondering if we could change that search to a regular expression filter so that "Walter" is ok, but things like ";alter" are marked [INVALID]? I'll tweak it on my end and post any updates I figure out, but thought I should also share my encounter with you. Thanks.
mf
KONDO

KONDO wrote on 07/24/09 9:48 AM

Hi, Thanks for the great components.
I use it in my JAPANESE website, where we have some submit button using japanese character for value.
the problem when user submit this button which has value in japanese character, portcullis treat it as a special character and block the ip address.
how can I modify portcullis to allow some value without blocking ?
Thanks.
Paul Stewart

Paul Stewart wrote on 01/18/10 9:11 AM

Hi, i am seeing the same problem with search engines as Mark. Can you pls?
John Mason

John Mason wrote on 01/20/10 3:08 PM

@Paul, @Mark

There's is an update to Portcullis, version 2.0.1 solves the problems you are seeing.
Paul Stewart

Paul Stewart wrote on 01/20/10 5:01 PM

Nice one John!
Wil

Wil wrote on 06/04/10 12:13 AM

Hi,
Just wondering if there is a way to exclude certain directories/files from using the CFC, the reason I ask is its breaking some of the XML code and would like to exclude this particular directory
John Mason

John Mason wrote on 06/15/10 2:20 PM

@Wil, it would be far better to see what exactly is breaking and fix that first. At this time, there's no function to opt out directories/files.
Mike

Mike wrote on 03/17/11 2:02 PM

Hi,

Does the variable scope in CF have thread-affinity? I was just wondering about the isDetected method. If the CFC is stored in the application scope, wouldn't isDetected() return the value set by the last thread (request) and not necessarily the running thread, given a multi-threaded architecture?

Would you not have to return the result directly from the scan() method to have a request-based result?
John Mason

John Mason wrote on 03/17/11 9:47 PM

@Mike - it entirely depends on how you call it. Normally, yes it would be used on each incoming request since that is coming from an outside client and can't be directly trusted. You could use it in cfthread (as in a multi-threaded arch), but I would suspect that would be internal to your application and therefore trusted.
Phil Williams

Phil Williams wrote on 04/13/11 4:01 AM

Hi John,

We're looking at adding this into our eCommerce application and have set the init command into the application.cfm file so in theory it should then fire for every request.

Now I tested this by deliberately adding script tags into a URL and the request didn't get blocked.

I'm just wondering if there is something I'm missing or better still, a way to test if the application is working as intended?

Kind regards,

Phil Williams
John Mason

John Mason wrote on 04/13/11 5:33 PM

@Phil - Are you also calling the scan() functions? The init() just loads the app into memory. The scan() calls do the actual scan.

Write your comment



(it will not be displayed)