URLScan3.1 & sesConverter for Coldfusion

Today I was doing some spring cleanup on my server and as part of this I applied URLScan to my webserver to enhance the security of IIS. It’s a simple & free install and all looked good. I checked the various sites running on the server and all seemed fine. Except… for my main site. I browsed to it and I got a 404. Every page returned a 404. Hmmm. Obviously I checked to make sure I hadn’t inadvertently deleted any files on the server while I was doing my cleanup. I hadn’t. IIS running properly? Well, since this was isolated to one site , yep. So it’s something about this one site that is broken and what could it be.

Some may know that I use a modified version of Fusebox3 framework for my coding. This routes everything through a single index.cfm file using various templates & includes. This makes maintenance simple and allows for a ton of code reuse and I’m a big fan.

To start the diagnosis, I simply started with index.cfm and commented out the code which kicks off the fusebox framework. A simple <cfoutput>#now()#</cfoutput> confirmed that I was able to serve a cfm page correctly and eliminate a CF configuration problem. Next I worked my way through a couple of more templates until I got to one which made me say hmmmm. As part of this framework, I redirect users who land on

mysite.com/index.cfm

to

mysite.com/index.cfm/fuseaction/main.home.html

When I removed the redirect so the page stayed on mysite.com/index.cfm my layout page loaded without the 404. Very interesting.

For years, I’ve been using sesConverter (on this one site only) to convert my  search engine friendly urls like:

index.cfm/fuseaction/circuit.action.html

to Coldfusion urls like

index.cfm?fuseaction=circuit.action

So it seems that was only files within the framework that seemed to be an issue. On a hunch, I tried

mysite.com/index.cfm?fuseaction=main.home

and TADA – the page loaded completely. OK. So now I knew there was an issue with sesConverter but what was it. The page at Fusium is pretty much abandoned with little information in any case. I had a look at the page code and didn’t really see anything that could be an issue.

I knew there was an issue with the url and I knew I had just installed URLScan so hmmmm what to do. Back to the URLScan site. All of the settings for URLScan are in URLScan.ini. Third setting in and I had my answer

“AllowDotInPath=0
By default, this option is set to 0. If this option is set to 0, URLScan rejects any request that contains multiple periods (.). This prevents attempts to disguise requests for dangerous file name extensions by putting a safe file name extension in the path information or query string portion of the URL. For example, if this option is set to 1, URLScan might permit a request for http://servername/BadFile.exe/SafeFile.htm because it interprets it as a request for an HTML page, when it is actually a request for an executable (.exe) file with the name of an HTML page in the PATH_INFO area. When this option is set to 0, URLScan may also deny requests for directories that contain periods.”

Since I left everything as default after the install, AllowDotInPath was set to 0 or deny. That’s a bit of a problem when your URL looks like

mysite.com/index.cfm/fuseaction/main.home.html

as the main.home will match the deny rule and URL Scan will block the request. (at least I know it’s working!) The fix was simple. Just change AllowDotInPath=0 to AllowDotInPath=1

No need to even restart IIS, The site was up and running immediately.

Note: Some may be wondering about the security of this. As it states in the note, allowing DotInPath could result in an attack like  “http://servername/BadFile.exe/SafeFile.htm. While this is true, an attacker has to have a way to get that file on your server in the first place. Since my server is locked down with no FTP and no user file uploads, there really is a very small risk in continuing to use sesConverter and AllowDotInPath=1

UPDATE: Found another issue today. Or rather a few browsers did.  I have a form that is filled out and before submit, I do a client side check to make sure the form is filled out correctly. If not, I pass a string to ColdFusion.Window.create to open a cfwindow with information on the missing fields

Basically it is this

ColdFusion.Window.create(“myWindow”+showreq.arguments[0], showreq.arguments[1], “#msgWindow.cfm?msg=<span style=”font-weight:bold;”>Require Fields Missing</span>” + fieldArray)

However, by default, URLScan blocks the inclusion of HTML in the url. This shows in the URLScan log as Rejected disallowed+query+string+sequence query+string – When users missed filling out a form field, instead of the nicely formatted error window, they received a 404 message (as the msgWindow.cfm was blocked) which led them to believe the page was broken, when in fact it was working, provided they filled out all the fields correctly.

So this kind of checking is something we want to keep doing as for the most part, HTML has no business in a URL. I will be re-thinking my user feedback on this and doing a bit of a rewrite but in the meantime, I needed a quick fix. Turns out URLScan.ini comes to the rescue again. Fortunately, there is a section called

 

[AlwaysAllowedUrls]
;
; URLs listed here will always be explicitly allowed by UrlScan
; and will bypass all UrlScan checks.  URLs must be listed
; with a leading ‘/’ character.  For example:
;
/msgWindow.cfm

and adding just the single URL to the exception rule, things got back to normal. Note that the path in the URL must be complete from the root so it may be  /files/display/msgWindow.cfm or whatever.

Wonder what I’ll find next?!?

 

 

Quick & Dirty QR Code Generator Using Coldfusion & cfimage tag

UPDATE #2 Please read my newer post which fixes a problem with the code below.

QR codes can be a great tool for marketing and engagement of customers. [UPDATE: But not without some risk ] I put together a simple Coldfusion page which allows you enter a url and get a QR code back. The code uses the Google Charts API and cfimage.

When you click create, we pass the value of the input field as a url variable to the Google API which returns a binary object. All we have to do is pass this result to cfimage  and write it to the browser. Done & Done. Works with CF8 & up

cfoutput>
<script type="text/javascript">
function doIT(){
self.location = 'dsp_qrcodeGen.cfm?siteurl='+document.getElementById('siteurl').value //+ '&pdf=' + document.getElementById('pdf').value;
}
</script>
<div style="margin:auto; width: 600px; height:450px;padding:25px;text-align:center;border:1px solid;">
<h3>QR Code Generator</h3>
<hr>
Input URL <input type="text" id="siteurl" style="width:500px;margin:50px 0 50px 0;"><br>
<button onclick="doIT();" id="btn">Create</button><br>
<cfif structkeyexists(url, "siteurl")>
<cfhttp url="http://chart.apis.google.com/chart?chs=200x200&cht=qr&chl=#url.siteurl#" result="qrcode" getasbinary="yes">
<cfimage action="writeToBrowser"
source="#qrcode.filecontent#" />
</cfif>
</div>
</cfoutput>

Stopping Comment Spammers & Email Harvesters with Coldfusion & Project Honeypot

UPDATE: I’ve added some additional code to improve performance, so after you’ve read this please check out the post Project Honeypot & Coldfusion Part 2

Over the last couple of months I’ve seen a huge increase in the number of comment spammers hitting a “Website Feedback” form on my site. I’ve been meaning to add reCaptcha to the form but hadn’t gotten around to it. And since reCaptcha may have been cracked, it might not be the best option anyways.

Anatomy of Comment Spam

The sender is usually a nonsense email like uqkropilz@spammer .net. One interesting thing I noticed was that initially, comments sent in had no links. They were simple things like

Comments: Hi there, I completely agree with what you r saying

My thought is that this seemingly pointless spamming was some kind of reconnaissance, however I don’t really know.  Now I wasn’t too worried about this as the spambot was only filling out a form which generated an email. However, after a period of a couple of weeks links started to appear and then more and more comments with more and more links arrived.

Comments: Hi there, Common sense is not so common. Goldm nze franc.ios idg imp 10 crona 1912 “persistant cough” “no fever” “no phlegm” Lincoln southwest emeralds Antonioorons Examen reglas de la carretera de la florida

The links were to sites that do who knows what. (Don’t worry, I’ve removed the links in the example). Generally the bot’s programmers would hope that their creation was hitting something that would be publicly viewable, like a blog comment. In my case, the spam was only coming to an email address, so it wasn’t doing what was hoped (unless I was foolish enough to click on a link). However, the volume that began to hit my inbox was beginning to get irritating.

Since this is a web form submission, I have been capturing the IP address (letting users know) and sending it with the feedback. With this data, I started creating a black list of my own, hardcoding them in my blocker.cfm to stop the bot from accessing pages on my site. The problem with this is that the IP’s would rotate so frequently that I was always adding new ones and maintaining the list in hardcoded format was becoming a pain. I thought about creating a table for the black list IP’s and then just doing a query against it. Before building something myself, I decided to see if there was something already out there. I try not to recreate the wheel whenever I can. I found a CFC on mximize.com which looked like it would do just what I wanted. (turns out it needed some tweaking as shown below but that’s ok 😉 The code is pretty straight forward but you need an API key from Project HoneyPot. This key is free, however you do need to participate in the project. There are several levels of participation, from installing a full honeypot to just telling some friends. All will get you a key. I decided to install a honeypot.

The Honeypot

Note that this isn’t a necessary step in setting up the comment spammer code, but I decided to participate because it’s a good project & it will help reduce spam on my site and on the web in general.

It’s very easy. They have a quick 4 step wizard which provides you some code in 9 different scripting languages. I, of course, chose coldfusion and was provided a custom CFM file to add to my site. Once you have uploaded the cfm to your webserver, you need to modify pages to add links to the CFM file they provided. The wizard gives you 8 examples of how to do this. It is recommended that the format vary to keep the bots from seeing a pattern. Adding these lines of code to dozens of pages would be a bit of a pain but I’m using a modified version of Fusebox, so I have layout files which all my various pages use. There are 9 of them for my site so I could have added a line to each and been done with it. However I have one file that is included on every page of  my site and that’s analytics.cfm, my code for Google Analytics. Adding the link code there would be the easiest except I needed to create a way to pull up different links on each page load. This is a simple thing using randrange() and cfswitch.

<!---project honeypot traps  --->
<cfset rnd = randrange(1,8)>

<cfswitch expression="#rnd#">
<cfcase value="1">
<a href="http://www.myserver.com/artisticelective.cfm"><!-- grandioso-flour --></a>
</cfcase>
<cfcase value="2">
<a href="http://www.myserver.com/artisticelective.cfm"><img src="grandioso-flour.gif" height="1" width="1" border="0"></a>
</cfcase>
<cfcase value="3">
<a href="http://www.myserver.com/artisticelective.cfm" style="display: none;">grandioso-flour</a> <div style="display: none;">
</cfcase>
<cfcase value="4"><a href="http://www.myserver.com/artisticelective.cfm">grandioso-flour</a></div>
<a href="http://www.myserver.com/artisticelective.cfm"></a>
</cfcase>
<cfcase value="5"><!-- <a href="http://www.myserver.com/artisticelective.cfm">grandioso-flour</a> -->
</cfcase>
<cfcase value="6">
<div style="position: absolute; top: -250px; left: -250px;"><a href="http://www.myserver.com/artisticelective.cfm">grandioso-flour</a></div>
</cfcase>
<cfcase value="7"> <a href="http://www.myserver.com/artisticelective.cfm"><span style="display: none;">grandioso-flour</span></a>
</cfcase>
<cfcase value="8"><a href="http://www.myserver.com/artisticelective.cfm"><div style="height: 0px; width: 0px;"></div></a></cfcase>
</cfswitch>

artisticelective.cfm is the file provided by the project and I use a random number between 1 & 8 to add a single line of code at each page load. This satisfies the requirement of the project and makes my life very simple. Note that should a human actually find and click one of these links it shows a Terms of Use page. Having this ToU page also provides you with some legal recourse in the (infinitesimally) small chance you could find and prosecute a spammer.

Now, on to the Comment Spam protection.

http:BL

http:BL is a Black List database of known and suspected comment spammers & email harvesters. It has an API which gives registered users the ability to check site visitors against the Project Honeypot black list. As noted in the FAQ, this does not contain blacklisted email servers, as we are dealing with bots & not rogue or open relay mail servers.

The BL API requires you to pass the DNS servers at dnsbl.httpbl.org an IP in reverse format. ie: 127.0.0.1 is passed as 1.0.0.127. The API passes back a string of octets in the form

127.3.5.1

where the first is always 127 (when query is properly formatted), the second is the number of days active as a tracked IP, the third is the threat score and the fourth is the type of visitor.

The full API docs are here.

The CFC does a several things and I’ve added hints to explain what each function is doing. Create a new CFC and drop it in your CFC directory. Note you need to replace YOUR_API_KEY with, well, your API key. There were a few problems with the code from mximize.com (perhaps CF version difference related?) so I made some small mods to the code to work (with CF8 at least).

 <cfcomponent displayname="DNS functions">
<!--- Original code from http://www.mximize.com/fighting-comment-spam-with-project-honeypot with modifications by JayB sidfishes.wordpress.com--->
 <cffunction name="honeypotcheck" returntype="struct" hint="Check Project HoneyPot http:BL">
  <cfargument name="ip" required="yes" type="string">
  <cfset var aVal = "">
  <cfset var hpkey = "YOUR_API_KEY">
  <cfset var stRet = structNew()>

  <!--- Get the different IP values --->
  <cfset aVal = listToArray(gethostaddress("#hpkey#.#reverseip(arguments.ip)#.dnsbl.httpbl.org"),".")>

<cfif aVal[1] eq "IP-Address not known"><!--- jb: added evaluation of array for good addresses --->
<!--- set a value indicating ok address --->
    <cfset stRet = {type=99}>
<cfelse>
  <!--- there was a match so set the return values --->
  <cfset stRet.days = aVal[2]>
  <cfset stRet.threat = aVal[3]>
  <cfset stRet.type = aVal[4]>

  <!--- Get the HP info message ie: threat level --->
  <cfswitch expression="#aVal[4]#">
   <cfcase value="0">
    <cfset stRet.message = "Search Engine (0)">
   </cfcase>
   <cfcase value="1">
    <cfset stRet.message = "Suspicious (1)">
   </cfcase>
   <cfcase value="2">
    <cfset stRet.message = "Harvester (2)">
   </cfcase>
   <cfcase value="3">
    <cfset stRet.message = "Suspicious & Harvester (1+2)">
   </cfcase>
   <cfcase value="4">
    <cfset stRet.message = "Comment Spammer (4)">
   </cfcase>
   <cfcase value="5">
    <cfset stRet.message = "Suspicious & Comment Spammer (1+4)">
   </cfcase>
   <cfcase value="6">
    <cfset stRet.message = "Harvester & Comment Spammer (2+4)">
   </cfcase>
   <cfcase value="7">
    <cfset stRet.message = "Suspicious & Harvester & Comment Spammer (1+2+4)">
   </cfcase>
  <!---  <cfdefaultcase> jb: moved to top of function as we can't
        eval the array if there is no lookup response ie: not match in http:BL
    <cfset stRet.message = "IP-Address not known">
   </cfdefaultcase> --->
  </cfswitch> 

</cfif>

  <cfreturn stRet>
 </cffunction>

 <cffunction name="gethostaddress" returntype="string" hint=
             "I do the dns lookup against the http:bl servers">
  <cfargument name="host" required="Yes" type="string" />
  <cfset var obj = "">
<cftry><!--- jb: added error handling as error is thrown if host
             lookup has no match in http:BL ie: it's not been reported as a problem --->
  <!--- Init class --->
  <cfset obj = CreateObject("java", "java.net.InetAddress")>
<cfset result =  obj.getByName(host).getHostAddress() >
<cfcatch type="any">
    <!--- an "error" in this case is an unknown address, which means it is not reported to http:BL --->
    <cfset result="IP-Address not known">
</cfcatch>
</cftry>
  <!--- Return result --->
  <cfreturn result>
 </cffunction>

 <cffunction name="reverseip" returntype="string" hint=" I return IP in reverse format as required by http:BL api" >
  <cfargument name="ip" required="Yes" type="string" />
  <cfset var aIp = listToArray(arguments.ip,".")>

  <!--- Return IP reversed --->
  <cfreturn aIp[4] & "." & aIp[3] & "." & aIp[2] & "." & aIp[1]>
 </cffunction>

</cfcomponent>

Once you’ve got your CFC on your server you just add this code to pages you want protected.

<!--- Check Project HoneyPot --->
<cfinvoke returnvariable="stCheck" method="honeypotcheck" component="com.HoneyPotdns">
<cfinvokeargument name="ip" value="#cgi.remote_addr#" /><!---jb: changed remote_host to remote_addr --->
 <!--- <cfinvokeargument name="ip" value="91.201.66.172" /> ---><!--- jb: known bad ip for testing --->
</cfinvoke>
<!--- Don't display the personal information --->
<cfif isDefined("stCheck") AND (stCheck.type GTE 4 AND stCheck.type LTE 7)>
  <!--- Send 404 message --->
  <cfheader statuscode="404" statustext="Not Found">
  404 Not Found
  <cfabort>
</cfif>

I added this code to blocker.cfm which is called on every page load. If the threat level returned by http:BL is 4 (or whatever you decide to set) then a simple 404 is returned and the comment spammer bot is stopped dead in it’s tracks.

All in all a pretty cool solution I think.