Ask Ben: Form Processing Demo With Anti-Spam Features

Posted February 15, 2007 at 8:50 AM by Ben Nadel

Tags: ColdFusion, Ask Ben

There is not a specific question here. Someone contacted me and was having trouble implementing one of the ColdFusion anti-spam examples that I put up. I have created this single page demo that includes both display and form processing so that people can see how it all fits together.

This is NOT meant to be bullet a proof anti-spam solution. This is meant as a demo to show some people how this might work. I hope that it helps. I have tried to put in good comments. The form makes use of a time stamp (an hour) for which the form is active. The idea is not to permanently stop forms from being submitted after an hour; the objective of the time stamp is to force a form refresh if the form is old. This is just meant to prevent spam bots from automatically submitting cached forms.

Hope this helps:

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!--- Param form variables. --->
  • <cfparam
  • name="FORM.first_name"
  • type="string"
  • default=""
  • />
  •  
  • <cfparam
  • name="FORM.last_name"
  • type="string"
  • default=""
  • />
  •  
  • <cfparam
  • name="FORM.email"
  • type="string"
  • default=""
  • />
  •  
  • <cfparam
  • name="FORM.comments"
  • type="string"
  • default=""
  • />
  •  
  • <cftry>
  • <cfparam
  • name="FORM.submitted"
  • type="numeric"
  • default="0"
  • />
  •  
  • <cfcatch>
  • <cfset FORM.submitted = 0 />
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!---
  • Param the anti-spam form values. These are the values
  • that we want the spam bots to submit by accident. These
  • are not going to be used for standard form useage.
  • --->
  • <cfparam
  • name="FORM.notes"
  • type="string"
  • default=""
  • />
  •  
  • <cfparam
  • name="FORM.referrer"
  • type="string"
  • default=""
  • />
  •  
  • <cfparam
  • name="FORM.spam_key1"
  • type="string"
  • default=""
  • />
  •  
  • <cfparam
  • name="FORM.spam_key2"
  • type="string"
  • default=""
  • />
  •  
  •  
  • <!---
  • Create an array to keep track of the form submissions
  • errors. This will help us determine if the form is
  • valid for submission.
  • --->
  • <cfset arrErrors = ArrayNew( 1 ) />
  •  
  •  
  • <!---
  • Set up an anti-spam key. This is what will be used
  • to encrypt and decrypt the spam key time stamp and
  • additional anti spam keys.
  • --->
  • <cfset strEncryptionKey = "azure_is_a_mega_babe!" />
  •  
  •  
  • <!---
  • Clean up the form data. While this is clearly not
  • necessary for any non-form posts, I like to do it here
  • just cause (I usually don't include in the form
  • processing page - it is a site-wide feature).
  • --->
  • <cfloop
  • item="strKey"
  • collection="#FORM#">
  •  
  • <!--- Trim all form values. --->
  • <cfset FORM[ strKey ] = Trim( FORM[ strKey ] ) />
  •  
  • <!--- Unescape any quotes. --->
  • <cfset FORM[ strKey ] = Replace(
  • FORM[ strKey ],
  • "&quot;",
  • """",
  • "ALL"
  • ) />
  •  
  • <!---
  • You could also do things here like replace out
  • Microsoft quotes with standard web quotes or
  • strip out M/N-dashes and replace with
  • standard dashes.
  • --->
  • </cfloop>
  •  
  •  
  • <!--- Check to see if the form has been submitted. --->
  • <cfif FORM.submitted>
  •  
  • <!---
  • The form has been submitted. Validate the form
  • data to make sure we have everything we need and
  • that this was NOT a Spam submission.
  • --->
  • <cfif NOT Len( FORM.first_name )>
  •  
  • <cfset ArrayAppend(
  • arrErrors,
  • "Please enter your first name."
  • ) />
  •  
  • </cfif>
  •  
  • <cfif NOT Len( FORM.last_name )>
  •  
  • <cfset ArrayAppend(
  • arrErrors,
  • "Please enter your last name."
  • ) />
  •  
  • </cfif>
  •  
  • <cfif NOT IsValid( "email", FORM.email )>
  •  
  • <cfset ArrayAppend(
  • arrErrors,
  • "Please enter a valid email address."
  • ) />
  •  
  • </cfif>
  •  
  •  
  • <!---
  • Check for spam bot red flags. Remember, since our
  • spam fields were hidden, standard users shoudl NOT
  • have seen them and therefore should not have filled
  • them out. However, as Spam bots see things that
  • standard users do not, they might fill them out. We
  • are also going to be checking our spam key. Since
  • this involved decryption let's put it in a CFTry /
  • CFCatch to handle any errors.
  • --->
  • <cftry>
  •  
  • <!--- Decrypt the encryption key. --->
  • <cfset FORM.spam_key2 = Decrypt(
  • FORM.spam_key2,
  • strEncryptionKey,
  • "CFMX_COMPAT",
  • "HEX"
  • ) />
  •  
  • <!--- Decrypt the time stamp. --->
  • <cfset FORM.spam_key1 = Decrypt(
  • FORM.spam_key1,
  • FORM.spam_key2,
  • "CFMX_COMPAT",
  • "HEX"
  • ) />
  •  
  • <!--- Check for spam. --->
  • <cfif (
  • Len( FORM.notes ) OR
  • Len( FORM.referrer ) OR
  • (NOT IsNumericDate( FORM.spam_key1 )) OR
  • (FORM.spam_key1 LT Now())
  • )>
  •  
  • <cfset ArrayAppend(
  • arrErrors,
  • "There was a problem with your form submission."
  • ) />
  •  
  • </cfif>
  •  
  •  
  • <!--- Handle any anti-spam errors. --->
  • <cfcatch>
  •  
  • <cfset ArrayAppend(
  • arrErrors,
  • "There was a problem with your form submission."
  • ) />
  •  
  • <!--- For Debugging: ---
  • <cfdump var="#CFCATCH#" />
  • <cfabort />
  • --->
  •  
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!---
  • Check to see if we have any form errors from data
  • validation. If we do, then the form errors array
  • will have a length. If this is the case, we do NOT
  • want to process any further. Instead, skip the rest
  • and then just re-show the form with the previous
  • data values.
  • --->
  • <cfif NOT ArrayLen( arrErrors )>
  •  
  • <!---
  • The form data is valid and found to have been
  • submitted by a standard user (not a spam bot).
  • At this point you can put it in the database,
  • send out emails, do what ever you want.
  • --->
  •  
  • </cfif>
  •  
  • <cfelse>
  •  
  • <!---
  • The form has NOT been submitted yet. Do any sort
  • of form initialization here.
  • --->
  •  
  • </cfif>
  •  
  •  
  • <!---
  • The actions here (after we have checed to see if the
  • form has been submitted) will take place no matter
  • what (unless processing has been haulted).
  • --->
  •  
  •  
  • <!---
  • Let's create a random key to encrypt the spam time
  • stamp (created next).
  • --->
  • <cfset FORM.spam_key2 = RepeatString(
  • ToString( Rand() ).ReplaceFirst( "^(\d+\.)?", "" ),
  • 2
  • ) />
  •  
  •  
  • <!---
  • Let's create a time stamp for this form so that if
  • it is cached, it cannot be submitted directly.
  • --->
  • <cfset FORM.spam_key1 = (
  • Now() +
  • CreateTimeSpan(
  • 0, <!--- Days. --->
  • 1, <!--- Hours. --->
  • 0, <!--- Minutes. --->
  • 0 <!--- Seconds. --->
  • )
  • ) />
  •  
  •  
  • <!--- Encrypt the spam key using our random number. --->
  • <cfset FORM.spam_key1 = Encrypt(
  • FORM.spam_key1,
  • FORM.spam_key2,
  • "CFMX_COMPAT",
  • "HEX"
  • ) />
  •  
  • <!---
  • Encrypt the encryption key using our global
  • encryption key. We will need this value AND the
  • global encryption key to decrypt the time stamp above.
  • --->
  • <cfset FORM.spam_key2 = Encrypt(
  • FORM.spam_key2,
  • strEncryptionKey,
  • "CFMX_COMPAT",
  • "HEX"
  • ) />
  •  
  •  
  • <!---
  • As one final thing before we render a page, we want to
  • make sure that our form data will NOT break the forms.
  • If any of our "Text"-based form values have quotes in
  • them, it will break the HTML. Escape all quotes.
  • --->
  • <cfloop
  • item="strKey"
  • collection="#FORM#">
  •  
  • <!--- Unescape any quotes. --->
  • <cfset FORM[ strKey ] = Replace(
  • FORM[ strKey ],
  • """",
  • "&quot;",
  • "ALL"
  • ) />
  •  
  • </cfloop>
  •  
  • </cfsilent>
  •  
  • <cfoutput>
  •  
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Anti-Spam Form Posting</title>
  • </head>
  • <body>
  •  
  • <!---
  • Check to see if we have any form errors to display.
  • We can check this no matter what as the variable
  • always exists. If the form has not been submitted
  • then the errors array is empty and will not show up.
  • --->
  • <cfif ArrayLen( arrErrors )>
  •  
  • <h3>
  • Please review the following errors:
  • </h3>
  •  
  • <ul>
  • <cfloop
  • index="intError"
  • from="1"
  • to="#ArrayLen( arrErrors )#"
  • step="1">
  •  
  • <li>
  • #arrErrors[ intError ]#
  • </li>
  •  
  • </cfloop>
  • </ul>
  •  
  • </cfif>
  •  
  •  
  • <form
  • action="#CGI.script_name#"
  • method="post">
  •  
  • <!---
  • This hidden form field is meant to flag that
  • the form has been submitted on the next
  • page submit.
  • --->
  • <input
  • type="hidden"
  • name="submitted"
  • value="1"
  • />
  •  
  • <!---
  • This is the time stamp before which the form
  • is still valid.
  • --->
  • <input
  • type="hidden"
  • name="spam_key1"
  • value="#FORM.spam_key1#"
  • />
  •  
  •  
  • <!---
  • This is the encrypted key that we used to
  • encrypt the timestamp.
  • --->
  • <input
  • type="hidden"
  • name="spam_key2"
  • value="#FORM.spam_key2#"
  • />
  •  
  •  
  • <label for="first_name">
  •  
  • First Name:
  •  
  • <input
  • type="text"
  • name="first_name"
  • id="first_name"
  • value="#FORM.first_name#"
  • maxlength="30"
  • /><br />
  •  
  • </label>
  • <br />
  •  
  • <label for="last_name">
  •  
  • Last Name:
  •  
  • <input
  • type="text"
  • name="last_name"
  • id="last_name"
  • value="#FORM.last_name#"
  • maxlength="40"
  • /><br />
  •  
  • </label>
  • <br />
  •  
  • <label for="email">
  •  
  • Email:
  •  
  • <input
  • type="text"
  • name="email"
  • id="email"
  • value="#FORM.email#"
  • maxlength="75"
  • /><br />
  •  
  • </label>
  • <br />
  •  
  •  
  • <input type="submit" value="Submit Form" /><br />
  •  
  •  
  •  
  • <!---
  • Now, here's where we put the anti-spam form
  • fields. These are referred to as honey pots.
  • They are hidden from the rest of the users.
  • I also like to put a comment here so that blind
  • people know the difference as well.
  •  
  • It also helps in case CSS is not working on
  • a given browswer.
  • --->
  • <div style="height: 1px ; overflow: hidden ; width: 1px ;">
  •  
  • <p>
  • Do NOT fill in the form fields below. They
  • are not meant to be used by people.
  • </p>
  •  
  • <label for="notes">
  •  
  • Notes:
  •  
  • <textarea
  • name="notes"
  • id="notes"
  • cols="40"
  • rows="10"
  • >#FORM.notes#</textarea><br />
  •  
  • </label>
  • <br />
  •  
  • Referrer:
  •  
  • <label>
  •  
  • <input
  • type="radio"
  • name="referrer"
  • value="Google"
  • />
  •  
  • Google
  •  
  • </label>
  •  
  • &nbsp;&nbsp;&nbsp;
  •  
  • <label>
  •  
  • <input
  • type="radio"
  • name="referrer"
  • value="Word of mouth"
  • />
  •  
  • Word of mouth
  •  
  • </label>
  •  
  • </div>
  •  
  • </form>
  •  
  • </body>
  • </html>
  •  
  • </cfoutput>

Please let me know if anyone else would like demo of anything.




Reader Comments

There are no comments posted for this web log entry.

Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 19, 2013 at 2:31 PM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
It's funny really just how well that image describes the way I would imagine most people that go with angular for some project is. I have had a similar roller-coaster ride with it as well, but not qu ... read »
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools