Protect Your Web Forms From Automated Spam

Status
Not open for further replies.

wdmny

New member
Jun 27, 2006
173
1
0
After my rant on stupid manual spammers, I thought I'd let you guys know some ways they you can stop automated form spam bots.

One way to trap spam bots is to name your obvious form fields like name and email to something that means nothing. Then you would create hidden form fields, but using the obvious names like name and email. The dumb form bot will likely fill in the hidden fields, which is easily detectable by your form processing script.

Code:
<form>
<input type="hidden" name="email">
<input type="hidden" name="name">
Name: <input type="text" name="elnombre1"><br>
Email: <input type="text" name="addr">
<input type="submit" value="Send">
</form>

There is another trick that requires Javascript, but will make it impossible for a bot to use your form. The form action is omitted so that the form will do nothing without the Javascript 'activating' it. The code would look like this:

Code:
<form name="TheForm" method="post">
<script language="Javascript">
document.onLoad = ActivateForm();
function ActivateForm() {
 objForm = document.forms['TheForm'];
 if (objForm) {
  objForm.action = 'FORMACTIONHERE.ASP';
 }
}
</script>
<!-- The Rest of Your Form Here -->
<input type="submit" value="Send">
</form>

Make sure to change FORMACTIONHERE.ASP to whatever your form action was before.

A final tip I have is on how to stop a particular bot that tries to hack your mail forms to broadcast spam to their email list. This bot tries to take advantage of security problems in certain mail scripts to turn your server into a spam relay. The fix for this requires you to edit your mail script and to have an understanding of how it works. If you cannot do this step, the above measures will also likely stop this bot.

There are two signatures of this bot. One is that it will put a few lines of garbage (MIME code actually) into fields like Email and Subject, which should only be one line. The way to catch this is to check for a valid email and make sure that the To, From, and Subject fields are no longer than one line.

The next signature is that it will use your own domain name for its email addresses. For example, it will claim its email is name@yourdomain.com. To trap this, make sure no one can use your domain name for their email address.
 
  • Like
Reactions: Ben


The problem with your first solution is that any competant form spammer can quickly write a script that will easily detect if the form is hidden or not. But who knows, you might fool the lots of them.

You could also use a captcha image verification script. That seems to work good.

If you're familiar with PHP, on the page of the form, create a session and input a random 5 letter value in that session. If you have GD library installed, just display that random 5 letter on in image and ask them to enter it. On the next page, compare what they wrote to the session value, and if it does not match, send them back.
 
You should use PHP or something. Unless you do some validation on the server you can't stop it.

One thing I do, I think I saw it on Schiflett or something is this (you have to use sessions). This passes a random token with the form and through sessions to your form processing script and verifies they are the same. This makes it pretty certain the form was submitted from your site. Someone could still sit there and spam manually of course, but that would likely be quite limited.

In the page with your form use these lines, asumming sessions active.

<?php
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
?>

In your form use (in php because of the var)

<input type='hidden' name='token' value='$token' />


Then in the script that processes and validates your form use this.

if ($_POST['token'] != $_SESSION['token']) {
echo "Get lost mofo!";
exit;
}

You must check all form data to make sure it is something you want and not bcc:, etc. Validate all email addresses, and strip all unwanted characters, don't forget stuff like 0x, base64, content-type:, etc.

You can also pass a time stamp and check it in the processing to make sure the form was submitted with xx minutes of the form being loaded.
 
  • Like
Reactions: Jan
Yoey - You're right about 'smarter' bots pickup up the hidden aspect. That's also why I recommend using weird names for your common fields. The only way for the bots to beat that is to fill in every field with a valid email address... a dead signature of a bot, just look for email addresses in weird fields.

The Javascript fix also avoids this as it makes it impossible for the bot to know where to post to. You can even better mask it by using a Javascript file instead of inline javascript. The bot will load your HTML page to find the form fields and the page to submit to, it won't parse the HTML out and download all the remote files as well. Ex:

Code:
<script language="Javascript" src="activateform.js"></script>

I still think you give spammers too much credit. I doubt most of them are writing their own scripts. If they were, tweaking bots on a per site basis to deal with anti spam measures like mine would be a time suck with little value.

Dew - The problem with your implementation of tracking the session is that you are passing the value to the bot. A smart bot would just post back all cookies and session values. The captchas yoey talks about address this problem because they hide the value from the bot.

Your comments on validation are dead on. The malicious bot that tries to turn your server into a spam relay submits encoded MIME to try to BCC out messages. They put it in the To/From/Subject fields because they are in the message header and can be hijacked (in some bad code).

I like your message to the bots. I'm doing something similar, trying to throw a wrench into the bots' system by sending them something unexpected.

John - The spammers im trapping with these techniques are really misguided. The messages aren't being broadcasted to a list or posted on the web anywhere, so they only benefit if I personally respond. Not going to happen. That said, the techniques would work for blog comments and the like just as well.

Anyone know the name of any common bots? I'd like to do some more recon and see how smart they actually are.
 
wdmny said:
Dew - The problem with your implementation of tracking the session is that you are passing the value to the bot. A smart bot would just post back all cookies and session values. The captchas yoey talks about address this problem because they hide the value from the bot.

A bot can not get your session, it could get the one in the form, so what? I think the risk of session hijacking for a form like this is pretty minimal. It's the fact that the token is passed both through the session and through the form that makes it work. So what if a bot gets the one through the form. If the form is not submitted from the site there will be no way for the numbers to match when the form processing scriot runs. These form hijackers are run from remote machines making this a bit dificult.

This is easier than a captcha as the user doesn't have to do anything extra.

Read here for more info on this.

http://shiflett.org/articles/security-corner-dec2004
 
How do I prevent my form from accepting content from a certain email address? I don't want to receive any mail from the address @hotbox.com

How do I configure my form so that it won't take this? (I dont know how to do this myself)

Thanks.
 
wickedDUDE said:
How do I prevent my form from accepting content from a certain email address? I don't want to receive any mail from the address @hotbox.com

How do I configure my form so that it won't take this? (I dont know how to do this myself)

Thanks.

What language is your form processing script? Maybe you can post some of the code here and we can help.
 
The form is written PHP.

Here is the script code:

Code:
<script Language="JavaScript">
function isEmailAddr(email)
{
  var result = false;
  var theStr = new String(email);
  var index = theStr.indexOf("@");
  if (index > 0)
  {
    var pindex = theStr.indexOf(".",index);
    if ((pindex > index+1) && (theStr.length > pindex+1))
 result = true;
  }
  return result;
}
function validRequired(formField,fieldLabel)
{
 var result = true;
 
 if (formField.value == "")
 {
  alert('Please enter a value for the "' + fieldLabel +'" field.');
  formField.focus();
  result = false;
 }
 
 return result;
}
function allDigits(str)
{
 return inValidCharSet(str,"0123456789");
}
function inValidCharSet(str,charset)
{
 var result = true;
 // Note: doesn't use regular expressions to avoid early Mac browser bugs 
 for (var i=0;i<str.length;i++)
  if (charset.indexOf(str.substr(i,1))<0)
  {
   result = false;
   break;
  }
 
 return result;
}
function validEmail(formField,fieldLabel,required)
{
 var result = true;
 
 if (required && !validRequired(formField,fieldLabel))
  result = false;
 if (result && ((formField.value.length < 3) || !isEmailAddr(formField.value)) )
 {
  alert("Please enter a complete email address in the form: [EMAIL="yourname@yourdomain.com"]yourname@yourdomain.com[/EMAIL]");
  formField.focus();
  result = false;
 }
 
  return result;
}
function validNum(formField,fieldLabel,required)
{
 var result = true;
 if (required && !validRequired(formField,fieldLabel))
  result = false;
 
  if (result)
  {
   if (!allDigits(formField.value))
   {
    alert('Please enter a number for the "' + fieldLabel +'" field.');
   formField.focus();  
   result = false;
  }
 } 
 
 return result;
}
 
function validInt(formField,fieldLabel,required)
{
 var result = true;
 if (required && !validRequired(formField,fieldLabel))
  result = false;
 
  if (result)
  {
   var num = parseInt(formField.value,10);
   if (isNaN(num))
   {
    alert('Please enter a number for the "' + fieldLabel +'" field.');
   formField.focus();  
   result = false;
  }
 } 
 
 return result;
}
 
function validDate(formField,fieldLabel,required)
{
 var result = true;
 if (required && !validRequired(formField,fieldLabel))
  result = false;
 
  if (result)
  {
   var elems = formField.value.split("/");
 
   result = (elems.length == 3); // should be three components
 
   if (result)
   {
    var month = parseInt(elems[0],10);
     var day = parseInt(elems[1],10);
    var year = parseInt(elems[2],10);
   result = allDigits(elems[0]) && (month > 0) && (month < 13) &&
      allDigits(elems[1]) && (day > 0) && (day < 32) &&
      allDigits(elems[2]) && ((elems[2].length == 2) || (elems[2].length == 4));
   }
 
    if (!result)
   {
    alert('Please enter a date in the format MM/DD/YYYY for the "' + fieldLabel +'" field.');
   formField.focus();  
  }
 } 
 
 return result;
}
function validateForm(theForm)
{
 // Customize these calls for your form
 // Start ------->
 if (!validRequired(theForm.name,"Name"))
  return false;
 if (!validEmail(theForm.email,"Email Address",true))
  return false;
 // <--------- End
 
 return true;
}
</script>

Please help me with this... I get bombarded with spam every time I check it, and it isnt stopping.

My form method is:

<form method="post" action="/action.php" onsubmit="return validateForm(this)" id="form1" name="form1">


Any help is greatly appreciated...
 
  • Like
Reactions: Jan
wickedDUDE said:
What's wrong with what I posted? This is the form script.
That's the processing script, not the form. I was wondering what fields you are using. Anyway, I'm certainly not an expert at PHP, but here is some stuff you can use. The token should prevent off-site spamming (sessions required). Change the text pattern to allow or dissalow characters in the name, subject and message. You can add other fields like date, time, etc.

Hope this helps. :)

The form (contact-form.php)
Code:
<?php
session_start();
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;  
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
</head>
<body>
<form action="process-form.php" method="POST" name="Contact us">
    Your email address: <input name="email" type="text" size="30" maxlength="100"><br />
    Your name <input name="name" type="text" size="30" maxlength="30"><br />
    Subject: <input name="sub" type="text" size="45"><br />
    Message: <textarea name="msg" cols="35" rows="10"></textarea><br />
    <input type="submit" name="submit" value="Send">
    <input type='hidden' name='token' value='<? echo $token; ?>' />
</form>
</body>
</html>
The processing script (process-form.php)
Code:
<?php
session_start();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
</head>
<body>
<?php
$email = (isset($_POST['email']) ? $_POST['email'] : '');
$name = (isset($_POST['name']) ? $_POST['name'] : '');
$sub = (isset($_POST['sub']) ? $_POST['sub'] : '');
$msg = (isset($_POST['msg']) ? $_POST['msg'] : '');
if ($_POST['token'] != $_SESSION['token']) {
    echo "Invalid data!";
    exit;
}

//    Validate email
$emailPattern = '/^[^@\s]+@([-a-z0-9]+\.)+[a-z]{2,}$/i';
if (!preg_match($emailPattern, $email)){
    print 'Please review the email address you entered. There seems to be a problem';
    exit();
}

//    Validate text
$textPattern = '/^[A-Za-z0-9()\/\'":\*+,.; \- !?&#$@\r\n]+$/';
if (!preg_match($textPattern, $name) || !preg_match($textPattern, $sub) || !preg_match($textPattern, $msg)){
    print 'Please review the information you entered. There seems to be a problem';
    exit();
}

// check for misc hacks
$ems = ''.$email.$name.$sub.$msg;
if ( stristr( $ems, 'content-type:' ) || stristr( $ems, 'multipart/mixed' ) || stristr( $ems, 'boundary=' ) || stristr( $ems, 'cc:' ) || stristr( $ems, 'multi-part message in mime format' ) || stristr( $ems, 'to:' ) || eregi( "(%[a-f0-9])", $ems ) || stristr( $ems, '0x' ) || stristr( $ems, 'base64'))
    exit();

echo "Form can be sent now.";
?>
</body>
</html>
 
Status
Not open for further replies.