Ask Ben: Form Processing Demo With Anti-Spam Features

Posted February 15, 2007 at 8:50 AM

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:

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

  • <!--- 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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




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

Reader Comments

There are no comments posted for this web log entry.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »
Nov 20, 2009 at 11:00 PM
Five Months Without Hungarian Notation And I'm Loving It
@Marcel, Yeah, I always err on the side of longer but more readable variable names. As for the camel casing of CF methods and the headless camel casing of custom items, I get around this by always ... read »
Nov 20, 2009 at 10:56 PM
Five Months Without Hungarian Notation And I'm Loving It
I use the following and love it: my.namespace.MyComponents.functionMethodsOrUDF() CONSTANT_VALUES_OR_PROPERTIES One thing I always try is to CamelCaseBuiltInColdFusionFunctions() so others can tell ... read »
Nov 20, 2009 at 5:38 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
Hi Ben, Great article. I've been looking around to see if ColdFusion image engine can programatically create the following "wrap around" effect: http://www.creativepro.com/article/photoshop-s-she ... read »
Nov 20, 2009 at 5:35 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Dave: I talked to Gert he suggested: <cfhttp method="get" url="http://{some cf website}" result="stuff" addtoken="yes" /> Note the addition of cfhttp attribute addtoken. That should persist y ... read »
Nov 20, 2009 at 5:23 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Todd, Ahh, gotcha, yeah that makes sense. ... read »
Nov 20, 2009 at 5:17 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
Ben, sorry if I didn't make this clear. You can make it work like that if you want, just put <cfset session.foo = 1> (and <cfset application.foo = 1>) in your OnRequestStart() and it reve ... read »