Ask Ben: Form Processing Demo With Anti-Spam Features
Posted February 15, 2007 at 8:50 AM 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 ],
- """,
- """",
- "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 ],
- """",
- """,
- "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"
- />
-
-
- </label>
-
-
-
- <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.



