Fully Accessible Spam-Form-Submission Blocking Using ColdFusion And (X)HTML (Version III)

Posted November 16, 2006 at 8:20 AM

Tags: ColdFusion

After some good discussions on CF-Talk about blocking spam bots from submitting web forms, I thought I would give it another go. Jacob Munson pointed out some issues with my previous solutions (v.1 and v.2) and Bobby Hartsfield made some great suggestions that I thought I would try out.

This solutions takes advantage of the fact that Spam Bots are just not that smart when it comes to form submissions (at least not yet). They tend to take a form and just submit all the fields. The smartest move they make is trying to figure out which form fields take which data (ie. urls, email address, names, etc.). But, even that, we can use against them.

The goal of this solution is to get the Spam Bot to submit data that a user would never submit. We then use this data to invalidate the form submission. To start with, I come up with a random key:

 Launch code in new window » Download code as text file »

  • <!--- Get the de-spamming key. --->
  • <cfset REQUEST.TestKey = (
  • "KS" &
  • Encrypt(
  • RandRange( -30, 30 ),
  • "SECRET_KEY_GOES_HERE",
  • "CFMX_COMPAT",
  • "HEX"
  • )
  • ) />

This just creates a randomly generated HEX key that is prepended with "KS" (for "Kinky Solutions"... and I didn't want it to start with a number - but just personal). Then, in the comment form I have:

 Launch code in new window » Download code as text file »

  • <form>
  • <input type="hidden" name="test_key" value="#REQUEST.TestKey#" />
  • <input type="hidden" name="test_email" value=" #REQUEST.TestKey# " />
  •  
  • <!---
  • REST OF THE FORM GOES [[ HERE ]].
  • THIS IS THE FORM THAT THE USER ACTUALLY SEES.
  • --->
  •  
  •  
  • <!--- THIS IS AFTER THE VISIBLE FORM FIELDS. --->
  •  
  • <div style="display: none ; visibility: hidden ;">
  •  
  • <p>
  • NOTE: Do Not Alter These Fields:
  • </p>
  •  
  • <label>
  • Contact Email:
  • <input
  • type="text"
  • name="contact_email"
  • value="<cfif StructKeyExists( FORM, "contact_email" )>#FORM.contact_email#</cfif>"
  • />
  • </label>
  •  
  • <label>
  • Contact Email:
  • <input
  • type="text"
  • name="contact_email2"
  • value=" #REQUEST.TestKey# "
  • />
  • </label>
  •  
  • <label>
  • Contact Url:
  • <input
  • type="text"
  • name="contact_url"
  • value="<cfif StructKeyExists( FORM, "contact_url" )>#FORM.contact_url#</cfif>"
  • />
  • </label>
  •  
  • <label>
  • Subscribe to Blog:
  • <input
  • type="checkbox"
  • name="contact_subscribe"
  • value="1"
  • <cfif StructKeyExists( FORM, "contact_subscribe" )>checked="true"</cfif>
  • />
  • </label>
  •  
  • <label>
  • Remember my Info:
  • <input
  • type="checkbox"
  • name="contact_remember_info"
  • value="1"
  • <cfif StructKeyExists( FORM, "contact_remember_info" )>checked="true"</cfif>
  • />
  • </label>
  •  
  • </div>
  •  
  • </form>

The form starts out by submitting the test key value (the randomly generated value). Then there is another hidden field titled "test_email". This is supposed to be the same value as the test key. I have padded the value with two spaces (on either side). When my forms get submitted, all fields automatically get Trim()'ed. This means that on the server, these two values will be the same, but in the HTML these two values will be different. I am just trying to do anything that might be confusing to the Spam Bot. Additionally, this field has "email" in the name which hopefully should entice the Spam Bot to alter its value.

Then, at the end of the form, I have a hidden DIV that has a number of fields that should remain untouched. For most users, the style attribute on the DIV will stop these fields from being displayed. For users who can't handle style, I have added the note "NOTE: Do Not Alter These Fields:". This should also prevent blind people from messing up.

I have ColdFusion CFIF statements for the FORM values in the hidden area because I have not CFParam'ed these values so they shouldn't exist in the FORM scope until the form has been submitted. I am taking the trouble to display any submitted value just in case the Spam Bot is actually looking at return value of the form, I want to persist the submitted value to make it seem like these fields are actually valid fields. If they kept showing up with the same default value after each form submission, a Spam Bot might be able to deduce that they were not being used????

I also tried to make the fields and field labels seems as valid as possible. I'm doing everything I can to trick the Spam Bot into changing the value of the form fields.

Ok, then on the server, I have some simple logic to check for a valid form submission. This is in my form data validation code (I have added spacing for better readability):

 Launch code in new window » Download code as text file »

  • <!--- The following is all for de-spamming. --->
  • <cfif (
  • Compare( FORM.test_key, FORM.test_email ) OR
  •  
  • (NOT StructKeyExists( FORM, "contact_email" )) OR
  •  
  • Len( FORM.contact_email ) OR
  •  
  • (NOT StructKeyExists( FORM, "contact_email2" )) OR
  •  
  • Compare( FORM.contact_email2, FORM.test_email ) OR
  •  
  • (NOT StructKeyExists( FORM, "contact_url" )) OR
  •  
  • Len( FORM.contact_url ) OR
  •  
  • StructKeyExists( FORM, "contact_subscribe" ) OR
  •  
  • StructKeyExists( FORM, "contact_remember_info" )
  • )>
  •  
  • <!--- Set error message. --->
  • <cfset REQUEST.FormErrors.Add(
  • "There was a problem submitting the form"
  • ) />
  •  
  • </cfif>

Then, of course, I don't allow the form to be processed if there are any error messages in my REQUEST.FormErrors collection. As you can see from this CFIF statement, none of the hidden form fields can be altered. Also, none of the hidden form checkboxes can be submitted.

This solutions has the following benefits:

  • It does NOT require Javascript to be enabled.
  • It does NOT require CSS to be supported.
  • It does NOT require any graphics (ie. CAPTCHA or other).
  • It does NOT block blind people from being able to participate.
  • It does NOT require any specific actions (ie. clicking on the submit button).
  • It does NOT require the user to do any additionally thinking!!!!

I think I implemented this about three days ago and I am happy to say that I did not get a single Spam Bot submission. Well, that's not true... I did get ONE, but it wasn't through the comments form. Some spam bot must have cached my old form and tried to submit it. Oddly enough this would work fine until I added the (NOT StructKeyExists( FORM, "XXXXX" )) conditions. This made sure that no old version of the form would pass the test (as old forms would not have the required fields). But as far as THIS form, no spam has gotten through.

So, so far, it seems to be working out quite nicely! Thanks to everyone on CF-Talk for their suggestions.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Nov 16, 2006 at 9:32 AM // reply »
2 Comments

This is an excellent solution to an ugly problem. I hate graphic CAPTCHA on web forms. Thanks!


Nov 16, 2006 at 9:46 AM // reply »
7,572 Comments

Trevor,

Don't thank me, there were a lot of people involved in this discussion... I am just posting the code that I used to implement it :)


Nov 16, 2006 at 9:47 AM // reply »
38 Comments

Congrats! After our discussion on cf-talk, I've decided to modify CFFormProtect too. I don't want to spill the beans yet, but my main goal is the same as yours, to let the user just fill out the form while stopping the evil spammers. I'm really glad we had that discussion, it really opened my eyes to a lot of issues.


Nov 16, 2006 at 9:49 AM // reply »
7,572 Comments

Jacob,

Yeah, it was a good conversation. Hopefully this comparatively simple solution should work fairly well.


Nov 16, 2006 at 11:28 PM // reply »
3 Comments

I like this idea a lot. As in that CF-talk discussion, I like the idea of a single field that says "Leave Blank" next to it rather than a whole section to be ignored though. If you know how blind users navigate (by headings and links and form elements often), that text could very well be missed. But if it's the form field's label it won't likely.

The best extension of the idea is that it can serve a dual-purpose: spam protection AND honeypot. You can easily add that user's IP to a blacklist (practically futile) and the comment to a quarantined area to be examined for content or patterns.

Lastly bots are often talked about like they are learning things. I think the way many "bots" get around countermeasures is because they are in fact user-configured scripts. That's why plain security through obscurity doesn't work. A spammer can check out your source and tell the script/bot what to plug in and then let it go to town on your site. So this could surely be defeated, but I think it's a great start.

If you banned all comments from a known list of anonymous proxies, then banning their IP would be even more effective.


Nov 17, 2006 at 9:46 PM // reply »
7,572 Comments

Doug,

I think the best solution would be to randomly select the form field names before the form itself was outputted. That way, on one load it might be "blogauthor" and on the next load it might be "author" and on the next load it might be "boogiemonster"... then I suppose you could send an encypted list (hidden form field) of the form fields that are valid.

But, I am not going to get that complicated until the spamming becomes an issue.


Nov 17, 2006 at 10:44 PM // reply »
3 Comments

Good ideas. I am with you though - I'd rather start simple and expand it if needed. It's actually a good learning experience to see if the spammers do defeat the easier to implement countermeasures. But it's nice to have a plan B for when that does happen too. Thanks for your thoughts on this matter.


Nov 21, 2006 at 7:52 AM // reply »
4 Comments

Has anyone else implemented this and seen great success? We just converted a contact form that was heavily spammed to a Flash Form and that seems to have helped. I would be interested in trying this approach as well.

Thanks.


Dec 12, 2006 at 6:16 AM // reply »
1 Comments

Are you're still bot-free after a month now? Good article!


Dec 12, 2006 at 7:11 AM // reply »
7,572 Comments

Byte,

I actually had some stuff making it through! I ended up blocking all comments that use the Link (A) tag in the comments. That with the combo of the other stuff have stopped all spamming so far.


Tom
Feb 5, 2008 at 6:21 PM // reply »
2 Comments

I am implementing this solution on our website, www.3dscanco.com on our main contact form as we've been getting a bunch of spam. One comment:

Shouldn't the last two "StructKeyExists" (for contact_subscribe and contact_remember_info) have a NOT in front of them?

Also, how is it working after 1+ years? Any holes yet?


Tom
Feb 5, 2008 at 6:26 PM // reply »
2 Comments

Actually, I just realized why it is like this. The checkbox being 0 is a good thing!


Feb 11, 2008 at 6:45 PM // reply »
7,572 Comments

@Tom,

So far, it has been pretty decent. I get spam sometimes, but I think that when it comes now, it is a person. Every now and then, I'll get like a string of 30 spams, but it's spread out over like 15 minutes, so I figure it's someone manually copy-n-pasting into the comments fields.


ka
Jul 21, 2008 at 4:05 PM // reply »
1 Comments

I understand what you are doing to keep spammers at bay. But not sure I understand the java class thing. You say ,"If an error exists" dont do any form processing, like we should know what to do tfrom there. I need help with the code. for stopping form processing.


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 20, 2010 at 12:07 PM
Drawing On The iPhone Canvas With jQuery And ColdFusion
Simply awesome. Saved my day. ... read »
Mar 20, 2010 at 9:00 AM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
I would like to say thx for an easy way to create a bottom bar. I do have a ?. Is it possible to center the bar if i want to resize it to ex 85%. Regards Offenbach ... read »
Mar 19, 2010 at 7:26 PM
MySQL 3/4 - com.mysql.jdbc.Driver And allowMultiQueries=true
Thank you very much for this post. Adding allowMultiQueries="true" in context.xml didn't help until I added it to url as allowMultiQueries=true Good idea is to use prepared statements and it will he ... read »
Jim
Mar 19, 2010 at 4:49 PM
Nobody Puts Baby In The Corner!
Wow. This is like suddenly finding a support group for your secret shame. I'm not alone! I always liked this movie, even though it is extremely cheesy. I just wish Jennifer Grey hadn't gotten the ... read »
Mar 19, 2010 at 4:47 PM
Application.cfc OnRequest() Method Affects OnError() Arguments
@Jason and @Ben, I've been doing some CF9 refactoring on our systems and noticed an odd occurrence with onError as well. Found a way to work around my problem, but what I saw was... Background: Our ... read »
Jim
Mar 19, 2010 at 4:44 PM
Shoot 'Em Up Starring Clive Owen And Paul Giamatti
I actually enjoyed this movie quite a lot. It was different, certainly, but I think they were going for more of a Quentin Tarentino-"wow, that was weird"-vibe than an actual spoof. Once I realize ... read »
Mar 19, 2010 at 4:34 PM
An Intensive Exploration Of jQuery With Ben Nadel (Video Presentation)
Hey I guess the video is down. Is there anyway you can upload to youtube or vimeo or some other service? Greatly appreciated. ... read »
Mar 19, 2010 at 4:24 PM
ColdFusion CFPOP - My First Look
@Ben Thanks for the follow up! The root of the problem had to do with being able to trace bounced emails to specific records in a DB table. Let's say you run an email campaign and you get 1,000 bou ... read »