In my last blog posting I alluded to using randomizing form field names as a solution to form attacks. Here is an example of how it can be created for a simple form page. There is no doubt more than one way to accomplish this kind of idea, so please this example only as a basic demo that suited my needs.
Create a moving target that attackers cannot seize upon repeatedly. build arrays in a looping construct for all the form fields you want to assign in your page. Store them in a PHP Session array. You use built-in php functions such as md5(), uniqid(), microtime(), mt_rand(), and a salt value if you like as well. You output your form fields dynamically, using php to assign the randomized hash to the name value of the form field. Enter some data, submit the form. The script takes your $_POST array and compares the array keys to $_SESSION. You can then do further validation and then assign your values to common sense variable names that are always private.
When you have validated this submission, you know the data has come from your form page. While you can spoof referrers, You cant spoof the form field names because they are only created at runtime.
The honeypot is the inverse approach, And also has lots of fans in its camp. A honeypot is a web form with addtional form elements, usually of a hidden type, that get discovered by a spammers crawler. They then seize upon the field name and use it in an attack. But since the form field isnt visible to users through the browser, it must be some kind of forged submission, and is worthy of filtering out.
The advantage of the moving target over honeypot is that forged submissions can be filtered out earlier in the script. Also, an attacker could easily analyze the form page once and determine what form fields to omit, and just add that information into the submitting script. They visited the page once, made a correction, and are back in business. Even so it is known as a successful defense. It is a successful defense because of the reason spam is spam: people messing with your site without ever even visiting it, not once. And if you are using an off-the-shelf website-in-a-box like WordPress or Drupal or whatever, the attacker can even more easily attack your site, with its cookie cutter template form elements, one same as the other million out there already.
It is very economical to attack as many sites as possible in the same way as possible. It will always be so.
I have had my share of naysayers over the moving target method. Please allow me reply to a few of the comments others have already made.
Why not just use the form name, why all form fields? I guess you could, but really there are a couple answers. First is the concept of defense in depth. Secure the whole thing, not just one element that an attacker could lock on to. Next answer is that it is simple enough to do the work in php to generate all the form field names you wish.
The site could still be attacked. Yes. Assume that it will be. Funky forms is of course not the only line of defense you must apply to stop your site from being trashed. What I was able to accomplish here is to break the link between the site and the garden variety automated attack, which must assume to know your form name and names of input fields in order to forge the rest of the information. The client must be on your web page in real time to submit data into your form. And in fact that is all the moving target approach does. The attacker still harvests your page, prepares a http remote attack in the guise of a simulated form posting, then goes to work, submitting to all the websites. But nothing gets through to a site with the moving target approach because field names wont match up.
A position based attacker could still hit it. Yes but of course you are not done validating your input because you have this in place. Spam, like anything else, is a matter of economics, in terms of both time and money. Yes someone could get you, but not likely, because like 2 boxers in a ring, both have to be stationary for a moment for a punch to connect. Otherwise its much harder to be effective, and much less powerful. The analogy is a fair one: The time required to hit a site with moving target is greater than the time to perform the usual kind of automated crawling and submitting designed for static form field names. The mere fact that you require your user to be on your page, absolutely, is enough in itself for attackers not to bother changing its tactics for millions of websites, or to lose so much time to making an exception to you that it becomes uneconomical to do so. As it stands, they may never even know that their submission was unsuccessful. You can of course push suspicious submissions to Akismet.
Yeah but sessions are evil and should never be used. Some have said so. Not to long ago, they didn’t work very well. But this isnt the case anymore. Drupal doesn’t use sessions, for example, and other middlewares avoid them as well. Projects with requirements for handling legacy code, particular kinds of services or policies may insist that sessions not be used. But even more evil is to never use sessions because of not understanding how to use them properly and parsimoniously.
First comes your form page, use some php before the form to generate the fields that you need.
<?php
session_start();
if(!$_SESSION["subscriber"]["values"]) {
$fieldNamesCount = 11;
$fieldNamesArray = array();
for ($i = 0; $i < $fieldNamesCount; $i++) {
// $fieldNamesArray2[] = md5("killSpam" . uniqid(microtime(), 1)); // random coctail with salt, if you wish
$fieldNamesArray[] = uniqid(md5(mt_rand())); // random coctail
}
$_SESSION["subscriber"]["fieldNames"] = $fieldNamesArray;
} else {
// do something when its a return pag
}
echo "<pre>";
print_r($fieldNamesArray)
echo "</pre>";
……. and then your form fields look something like this:
Name: <input type="text" name="{$_SESSION["subscriber"]["fieldNames"][0]}" value="<?php echo $_SESSION["subscriber"]["values"][0]; ?>" size="20" maxlength="50" />
Phone: <input name="{$_SESSION["subscriber"]["fieldNames"][1]}" type="text" value="<?php echo $_SESSION["subscriber"]["values"][1]; ?>" size="20" maxlength="20" />
You submit this to your form target script. If you look at your page Info in Firefox, under the forms tab, you will see you have form field names created from random hashes generated at runtime. The values for the names will be unique at every page load. The user must be on the page to submit.
So lets take a look at the script you are posting this data to.
Lets just assume that you are pointing this form submission to a different file, so here is what is required at a minimum:
<?php
session_start();
if (!$_POST) {
echo "no post reference";
exit();
}
// compare $_SESSION["subscriber"]["fieldNames"]
// to array_keys($_POST);
if(!$_SESSION["subscriber"]["fieldNames"]) {
echo "no ref to my session";
exit();
}
$postedKeys = array_keys($_POST); // I need to access this as an array.
$_SESSION["subscriber"]["values"] = $_POST;
$realNames = array('Name','Telephone',.... etc);
for($i = 0; $i < count($postedKeys); $i++) {
if($postedKeys[$i] == $_SESSION["subscriber"]["fieldNames"][$i]) {
// no cheating! you must you my randomly generated field names to use this page!!!!
$realValues[$realNames[$i]] = $_SESSION["subscriber"]["values"][$_SESSION["subscriber"]["fieldNames"][$i]];
} else {
// its the work of satan
echo "please dont do that ";
exit();
}
}
so if it passes all the tests, its good to go. Otherwise, its like two people talking to each other who dont speak each others language. They will never get what each other is saying, will never understand, and will just move on.