Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with:

Ask Ben: Form Processing Demo With Anti-Spam Features

Posted by Ben Nadel

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

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.