Ask Ben: Form Processing Demo With Anti-Spam Features
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.
Want to use code from this post? Check out the license.
Reader Comments