Humans are inherently slower than computers when it comes to reading and filling out a form. Even simple login forms where everything is auto-completed and you just have to click the “login” button takes a second, while a computer can do it in milliseconds. More complex forms require even more time to for a human to read, understand and complete. Recoding the timestamp of the form request and requiring the response to occur with a set range makes the automatic completion of the form more expensive for a spambot.
The timestamp can be sent along with the normal form fields as a hidden input field, so long as the validity of the timestamp is checked when validating the form submission. The easiest method is a HMAC check with a server-specific key. This actually allows for integration of additional data into the timestamp field, like the requester’s IP address and User agent.
Example Creation and Validation of a Form Timestamp
// globals
$gvServerPublicFormKey = '5f4dcc3b5aa765d61d8327deb882cf99';
//! Create Timestamp Field
//! @return string HTML of timestamp field
function createTimestampField(){
global $gvServerPublicFormKey;
// get current unix timestamp
$t = time();
// compose raw value as time:IP:user agent
$data = $t . ':' . $_SERVER[‘REMOTE_ADDR’] . ':' . $_SERVER['HTTP_USER_AGENT'];
// generate HMAC hash
$hash = hash_hmac('sha512', $data, $gvServerPublicFormKey);
// build input
$html = "";
return $html;
}
//! Validate Timestamp Input
//! @param int $min Minimum delay time in seconds @default[5]
//! @param int $max Maximum delay time in seconds @default[1200]
//! @returns bool Returns the validity of the timestamp input field
function validateTimestampInput($min = 5, $max = 1200) {
global $gvServerPublicFormKey;
$t = 0;
$hash = '';
// field field
foreach(($_REQUEST as $key => $val) {
if(strpos($key, 'ts-') !== 0) continue;
$t = substr($key, 3);
// validate potential timestamp value
if(!$t
|| intval($t) != $t
|| $t + $min < time()
|| $t +$max > time()
) {
continue;
}
$hash = $val;
break;
}
// potentially valid timestamp not found
if(!$hash) return FALSE;
// generate hash based upon timestamp value
$data = $t . ':' . $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['HTTP_USER_AGENT'];
$correctHash = hash_hmac('sha512', $data, $gvServerPublicFormKey);
// return validity of hmac hash
return hash_equals($hash, $correctHash);
}