"Innovation comes only from readily and seamlessly sharing information rather than hoarding it."- Tom Peters
Development Blog

Form Security using PHP

 Spammers attack in many different ways.  One way to avoid spam attacks is to make hidden elements that appear to be legitimate inputs.  Normal human users cannot see these elements, and will therefore, not fill them in.  If filled, you know it must be a bot.  This class is a sample of how to do this.


What we will do is allow a user to pass an array of element "names" that they want to be hidden.  Some examples might be "firstName", "lastname", "email", or "username".  Our PHP class will return these elements as hidden input elements, and then store them into a session variable.  After the form is submitted, the class will check all of these elements to see if any are filled.  If any are filled, we know something has happened.  So... let's begin.

First, we need to build our PHP class.  Here's the barebones...

<?php

class safeForm {
     //This variable will store the current data.
     private $data = array();    

     //This variable will store the old data to be checked.
     private $old_data = array();

     //This function will actually do the input element creation.
     function setFields($fieldNames) {

     }
}

What you see are our two variables, one to store our current data, and one to store the old data. Our function setFields() will actually do the creation of our hidden elements. The variable $fieldNames will be an array of string elements of the field names to hide.

Now, we must think of how we will actually hide our elements.  There are a few ways to do this.  You can either use inline CSS styling, or you can make a class that will hide the elements.  For the most part, spammers will not look up your class names and check to see if these elements are hidden.  For simplicity's sake, I will use inline styling here.  Let's do it.

//This function will actually do the input element creation.
function setFields($fieldNames) {
	$output = "";
	
	//Loop through each element in the array.
	foreach($fieldNames as $field) {

		//Create the HTML output.
		$output .= "**Do not enter anything here.\n";

		//Add the field name to our data array.
		$this->data[] = $field;
	}

	//Return all HTML for the hidden fields.
	return $output;
}

You might have noticed in the $output variable that there is another hidden span. This is to give a warning to anyone that may (for some really stranger reason) have CSS turned off. Each time we run through the loop, we add the field name to the $data variable. Now, we need to save this into our session. One of the easiest ways will be to do this in the classes deconstruct function. That way, we don't hit the session variable multiple times; we can do it just once.

//Function run as class is destroyed.
function __destruct() {
	$_SESSION['formData'] = serialize($this->data);
}

We serialize the data to make storing of the array easier, and more efficient.

Inputting the data into our form

Now that we can create our bogus, hidden fields, we need to put them into a form. Let's do it. This will go at the top of your form page.

<?php
//Start the session.
session_start();
//Include the class file.
include('safeForm.class.php');
//Create a class object.
$safeForm = new safeForm();
?>

Then, in the form, all we have to do is call our setFields() function, and pass it an array of element names we want to create.

<?php print $safeForm->setFields( array( 'name', 'firstname', 'email', 'website') ); ?>

That's all there is to it! The PHP function above will create hidden input elements for each of string names... it will be outputted as follows...

**Do not enter anything here.
**Do not enter anything here.
**Do not enter anything here.
**Do not enter anything here.

Validating the inputs

Now, all we have to do is validate the form. Remember, we are checking to see if any of these inputs have been filled in. Since they are hidden to the normal user, they should not be filled. If they are, it's safe to say that a bot filled them in. So, add the following function to set up the $old_data variable.

//Store the old_data, if available as the class is constructed.
function __construct() {

	//If session data is available, set the data.
	if (isset($_SESSION['formData'])) {
		$this->old_data = unserialize($_SESSION['formData']);
	}
}

The session data had to be unserialized before saving, to turn it back into the array it originally was.

Now, let's actually do the form validation. To do so, we need to run through the old_data, and look to see if any are filled.

//Validate the input fields.
function validateFields() {
	//Loop through each element name in old_data.
	foreach($this->old_data as $field) {
		//Is the element filled?
		if (isset($_POST[$field]) && $_POST[$field] != "") {
			return false;
		}
	}
	//No problems were found, so return true.
	return true;
}

Now, just add it to the HTML page, and we're basically done!

//If a form has been submitted.
if (isset($_POST)) {

	//Validate the fields
	if ($safeForm->validateFields()) {
		//Other validation methods, message, etc.	
	}
	else {
		$error = "There was an error";
	}
}

If you want, you can place a message output in your HTML to let you know if there was an error.

<?php if ($error) print $error; ?>

Now, load up your form, and submit it. You should get no error. Then, turn on TamperData (Firefox plugin available here) and affect one of your other fields. It should then print out an error. Of course, you can easily adjust how the error handling works, etc.

Here's the entire code:

PHP Code:

<?php


class safeForm {
     //This variable will store the current data.
     private $data = array();    

     //This variable will store the old data to be checked.
     private $old_data = array();

	//Store the old_data, if available as the class is constructed.
	function __construct() {

		//If session data is available, set the data.
		if (isset($_SESSION['formData'])) {
			$this->old_data = unserialize($_SESSION['formData']);
		}
	}

	//This function will actually do the input element creation.
	function setFields($fieldNames) {
		$output = "";
		
		//Loop through each element in the array.
		foreach($fieldNames as $field) {

			//Create the HTML output.
			$output .= "**Do not enter anything here.\n";

			//Add the field name to our data array.
			$this->data[] = $field;
		}

		//Return all HTML for the hidden fields.
		return $output;
	}


	//Validate the input fields.
	function validateFields() {
		//Loop through each element name in old_data.
		foreach($this->old_data as $field) {
			//Is the element filled?
			if (isset($_POST[$field]) && $_POST[$field] != "") {
				return false;
			}
		}
		//No problems were found, so return true.
		return true;
	}

	//Function run as class is destroyed.
	function __destruct() {
		$_SESSION['formData'] = serialize($this->data);
	}
	

}

?>

HTML Code

<?php
//Start the session.
session_start();
//Include the class file.
include('safeForm.class.php');
//Create a class object.
$safeForm = new safeForm();

//Do we have a validation error?
$error = false;

//If a form has been submitted.
if (isset($_POST)) {

	//Validate the fields
	if ($safeForm->validateFields()) {
		//Other validation methods, message, etc.	
	}
	else {
		$error = "There was an error";
	}
}


?>

<?php if ($error) print $error; ?>

<?php print $safeForm->setFields( array( 'name', 'firstname', 'email', 'website') ); ?>
AttachmentSize
files.zip1.29 KB