My clients and I have been receiving increasing amounts of spam sent through our own contact forms. Not being a spammer myself, I’m left to speculate on how one sends spam through a webmail form, but I’ve come up with two ways of preventing it from happening. Both of these methods involve editing the contact form’s HTML and adding a JavaScript file. They also require that legitimate users of the contact form have DOM-compliant browsers with JavaScript enabled.
Defeating human-like robots
For a very long time, I suspected that the spammers’ bots were filling out and submitting forms just like regular human visitors. They would look for input fields with labels like ‘name’ and ’email’, and, of course, for textarea elements. The bots would enter values into the fields and hit the submit button and move on to the next form.
To combat this, one could institute a challenge-response test in the form of a question that must be correctly answered before the form is submitted. Eric Meyer wrote a very inspiring piece at WP-Gatekeeper on the use of easily human-comprehensible challenge questions like “What is Eric’s first name?” as a way to defeat spambots. There are a number of accessibility concerns and limitations with this method, mostly with respect to choosing a challenge question that any human being (of any mental or physical capacity, speaking any language, etc.) could answer, but that a robot would be unable to recognize as a challenge question or be unable to correctly answer. However, these issues also exist with the CAPTCHA method.
In this case, the challenge question will be What color is an orange? If answered correctly, the form is submitted. If answered incorrectly, the user is prompted to try again.
Here’s how to implement a challenge question method of form validation:
First, create a JavaScript file named ‘validate.js’ with the following lines:
function validateForm()
{
valid = true;
if ( document.getElementById('verify').value != "orange" )
{
alert ( "You must answer the 'orange' question to submit this form." );
document.getElementById('verify').value = "";
document.getElementById('verify').focus();
valid = false;
}
return valid;
}
This script gets the value of the input field with an ID of ‘verify’ and if the value is not the word ‘orange’, the script returns ‘false’ and doesn’t allow the form to post. Instead, it pops up a helpful alert, erases the contents of the ‘verify’ field, and sets the cursor at the beginning of the field.
Add the JavaScript to your HTML with something like:
<head>
...
<script src="validate.js" type="text/javascript"></script>
...
</head>
Next, modify the form to call the function with an onSubmit
event. This event will be triggered when the form’s Submit button is activated. Add an input
field with the ID ‘verify’ and an onChange
event to convert the value to lowercase. Add the actual challenge question as a label.
<form id="contactform" action="../webmail.php" method="post" onsubmit="return validateForm();">
...
<div><input type="text" name="verify" id="verify" value="" size="22" tabindex="1" onchange="javascript:this.value=this.value.toLowerCase();" /></div>
<label for="verify">What color is an orange?</label>
...
</form>
A visitor to the site who fills out the form but does not correctly answer the challenge question will not be able to submit the form.
Defeating non-human-like robots
I believe that the challenge-response method is becoming less effective, however. According the article ‘Spamming You Through Your Own Forms‘ by Will Bontrager, the spammers’ bots are not using the form as it is intended.
This is what appears to be happening: Spammers’ robots are crawling the web looking for forms. When the robot finds a form:
- It makes a note of the form field names and types.
- It makes a note of the form action= URL, converting it into an absolute URL if needed.
- It then sends the information home where a database is updated.
Dedicated software uses the database information to insert the spammer’s spew into your form and automatically submit it to you.
His response is to stop the process at step 2 by eliminating the bots’ access to the webmail script. He suggests doing this by hiding the URL of the webmail script in an external JavaScript file, then using JavaScript to delay the writing of the form’s action
attribute for a moment. The robots parsing just the page’s HTML never locate the URL to the webmail script, so it is never available for the spammers to exploit.
While I like the idea, I think I’ve come up with a better way of implementing it.
First, rename the webmail script, because the spammers already know the name and location of that script. For example, if GoDaddy is your host, contact forms on your site may be handled by ‘gdform.php’, located in the server root. You’ll need to rename that to something else. For purposes of illustration, I’ll rename the script ‘safemail.php’, but a string of random hexadecimal characters would be even better.
Next, give your contact form an ID. If you are running WordPress or other blogging software, be sure to give the contact form a different ID than the comment form, or else the JavaScript will cause the comment form to post to the webmail script. I’ll give my contact form the ID ‘contactform’.
<form id="contactform" action="../gdform.php" method="post">
We want to prevent the spammers from learning about the newly renamed script. This is done by giving the URL to a fake webmail script as the form’s action attribute and using JavaScript to change the action
attribute of the form to the real webmail script only after some user interaction has occurred. I’ll use ‘no-javascript.php’ as my fake script.
To accommodate visitors who aren’t using JavaScript, the fake script could instead be a page explaining that JavaScript is required to submit the contact form and offering an alternate way to contact the author.
Edit the contact form’s action attribute to point to the fake script.
<form id="contactform" action="no-javascript.php" method="post">
Create a new, external JavaScript file called ‘protect.js’, with the following lines:
function formProtect() {
document.getElementById("contactform").setAttribute("action","safemail.php");
}
The function formProtect, when called, finds the HTML element with ID ‘contactform’ and changes its ‘action’ attribute to ‘safemail.php’. Obviously, one could make this script more complex and potentially more difficult for spammers to parse through the use of variables, but I don’t see that as necessary at this point.
Add the JavaScript to your HTML with something like:
<head>
...
<script src="formprotect.js" type="text/javascript"></script>
...
</head>
Finally, call the script at some point during the process of filling out the form. Exactly how you want to do this is up to you, and it’ll be effective longer if you don’t share how you do it. Perhaps the most straight-forward way would be to call the script at the point of submission by adding onsubmit="return formProtect();"
to the <form>
element.
<form id="contactform" action="no-javascript.php" method="post" onsubmit="return formProtect();">
If you want to use both the challenge question and the action rewriting functions, you may want to combine them into a single file or trigger formProtect separately with an event on one of the required input fields. If you decide to trigger formProtect with an event other than onsubmit, consider usability/accessibility issues—not everyone uses a mouse.
In conclusion
By implementing both of these methods, it is possible to dramatically reduce or even completely stop contact form spam. In the two months since I implemented this system, I haven’t received a single spam email from any of my contact forms.
The challenge-response test should deter or at least hinder human spammers and robots that fill out forms as though they were human. The trade-off is some added work for legitimate users of the form.
The action attribute rewriting method should immediately eliminate all spam sent directly to your form by spammers who have the URL of your webmail script in their databases. It should also prevent the rediscovery of the URL. Visitors with JavaScript enabled won’t be aware of the anti-spam measures.
For WordPress users
Defeating WordPress comment spam explains how to apply the attribute rewriting method to your WordPress site.