Combatting Form Spam

Over the last few weeks we’ve had to field several emails from disgruntled clients, annoyed at spammers abusing their website. It turns out that spammers have been trying to hijack contact forms and use them to send out unsolicited email.

The contact forms on our clients sites are simple PHP scripts which format a POST request into an email message and fire it off to a specified account. We’ve found that spammers send bogus submissions with the input fields littered with crude attempts to inject bcc: values in the header and change the content-type of the message (I assume to send attachments?). Anyway, it doesn’t work as all the headers are hard-coded and resistant to injection, but it doesn’t stop the scumbags trying, resulting in our clients getting lots of junk emails (sometimes hundreds each day) from their website.

After speaking to our ISP and doing some research on the web, we stumbled across a clever little PHP function on php.net which cuts down on such nonsense. Essentially the function intercepts all POST requests and scans each value for a series of suspicious strings (such as content-type:, charset=, mime-version:, bcc: etc.). There are also a number of environment checks which help filter out the scumbags, such as a missing HTTP_USER_AGENT value — which almost always indicates a spammer.

The easiest way to deploy the script is to save it somewhere in your webspace and include it in every page (either by adding it to your template or using a php_value rule in a htaccess file). Ideally your ISP would put this somewhere global and do it for you, but it’s easy enough to do yourself.

We’ve been running it on some of our clients sites for a few weeks now, and has saved us several potential headaches, and, more importantly, kept our clients happy.

Posted 4 years, 6 months ago

One way I used to do it, was to have the script seperate to the (for instance) contact_us.asp.

Then, once the script was called, I used to have it check that the referrer was the contact_us.asp page using Request.ServerVariables("HTTP_Referer"). If not, the form submission was rejected.

Not sure how secure it really was (as it’s fairly easy to fake HTTP headers) but it seemed to stop most, if not all, of the bogus attempts.

I suppose you could also include a parameter within the form that is generated when the page renders ... once submitted with the form, it could be used as a key against a password on the server, so therefore authorising the form for use.

Interesting stuff.

Steven Woods · www · 4 years, 6 months ago

I’m going on the assumption that form spammers are probably not GET'ing the form before they POST it. Istead, they GET it once, cache the form fields in their spam database, and POST to the form handler whenever they want. My Apache logs seem to bear that out.

So, as the previous poster mentioned, I’m going to include a dynamically generated hidden field as a key in my form. It’ll be nothing special - just an integer made up of (month * day * year). Then on the server, I’ll divide it out again. If $key / $today_month / $today_day != $today_year, I’ll assume it’s a cached form submitted by a spammer and silently reject it.

To accomdate any legitimate postings that may happen over midnight, I’ll allow the key to be off by one day. And of course this is all easily decoded, but I’m assuming that spammers aren’t carefully analyzing the forms they hijack before sucking them into their database.

We’ll see!

George Adams · 4 years, 3 months ago

Commenting on this post has been disabled.