Solved

Exit PHP if form is submitted in less than a specified period of time

Posted on 2014-07-21
32
429 Views
Last Modified: 2014-07-22
I'm using a PHP form handler to validate form fields on a contact page which has been abused by spam bots. There is an invisible text field on the form, named "name".  The PHP redirects to a "thankyou" page and exits if that field is populated.  To augment this,  I'd like the PHP to redirect to another page, and exit -- if the form is submitted within a specified number of seconds -- instead of completing its usual tasks.  Can you add the required code to this snippet?  

<?php 
if($_POST['name'] != '') {  //show thankyou page and exit.
    header("Location: thankyou.htm"); /* Redirect to thankyou page */
    exit; }
$errors = '';
$myemail = 'stay@mauitradewinds.com';//<-----Put Your email address here.
if(empty($_POST['firstname'])  || 
   empty($_POST['lastname'])  ||
   empty($_POST['email']) || 
   empty($_POST['message']))
{
    $errors .= "\n Error: At a minimum, we need your name, Email address and message in order to transmit your form.";
}
else
{
    $firstname = array_key_exists('firstname',$_POST) ? $_POST['firstname']:''; 
    $lastname = array_key_exists('lastname',$_POST) ? $_POST['lastname']:''; 
    $Address = array_key_exists('Address',$_POST) ? $_POST['Address']:''; 
    $City = array_key_exists('City',$_POST) ? $_POST['City']:''; 
    $State = array_key_exists('State',$_POST) ? $_POST['State']:''; 
    $Zip = array_key_exists('Zip',$_POST) ? $_POST['Zip']:''; 
    $Country = array_key_exists('Country',$_POST) ? $_POST['Country']:''; 
    $Phone = array_key_exists('Phone',$_POST) ? $_POST['Phone']:''; 
    $email_address = array_key_exists('email',$_POST) ? $_POST['email']:''; 
    $message = array_key_exists('message',$_POST) ? $_POST['message']:''; 
    if (!preg_match(
    '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i', 
    $email_address))
    {
        $errors .= "\n Error: Invalid email address";
    }
}

if( empty($errors))
{
	$to = $myemail; 
	$email_subject = "Contact form";
	$email_body = "You have received a contact form from Site-1. ".
	" Here are the details:\n First Name: $firstname \n Last Name: $lastname \n Address: $Address \n City: $City \n State: $State \n Zip: $Zip \n Country: $Country \n Phone: $Phone \n Email: $email_address \n Message: $message"; 
	
	$headers = "From: $myemail\n"; 
	$headers .= "Reply-To: $email_address";
	
	mail($to,$email_subject,$email_body,$headers);
	//redirect to the 'thank you' page
	header('Location: thankyou.htm');
	exit;//You should always "exit" immediately after a redirection request
} 
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>
<head>
	<title>Contact form handler</title>
</head>

<body>
<!-- This page is displayed only if there is some error -->
<?php
echo nl2br($errors);
?>


</body>
</html>

Open in new window

0
Comment
Question by:ddantes
  • 13
  • 10
  • 4
  • +3
32 Comments
 
LVL 21

Expert Comment

by:Randy Poole
Comment Utility
On your actual form you would add a cookie specifying when the user loaded the form, then check the value of that cookie against your submit form and perform the calculation.
0
 

Author Comment

by:ddantes
Comment Utility
Thank you.  I'd appreciate it if you could specify the cookie code for the contact form, and insert the required code into the PHP snippet.  But, before you invest time in that, are you confident that spam bots accept cookies?
0
 
LVL 34

Expert Comment

by:gr8gonzo
Comment Utility
Nowadays, two pretty effective anti-bot techniques are:

1. Use Javascript to populate a hidden field with some kind of value that is checked on the receiving side. Bots do not typically run Javascript, so that code will usually not be run. Since it's all hidden, normal users won't have to do anything different. It's not 100% fool-proof, but it's pretty effective, especially if you use more complicated Javascript.

2.  Use CAPTCHA. Easy to implement nowadays and pretty safe and proven.

I wouldn't suggest using cookies or time-based techniques, because you are likely to end up upsetting people that take too long or not long enough. For example, you might have someone typing in a really short or a really long message and then they get frustrated when they're treated as a bot.
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Agree with Randy but just set a cookie with a short expiry time say 30 seconds, at the server check the cookie exists

setcookie("contactform", "1", time()+30);

Then
if(isset($_COOKIE["contactform"])){ // cookie is set
...
}
0
 

Author Comment

by:ddantes
Comment Utility
Thank you.  I'm using a hidden text field called "name", which would only be populated by a machine.  If that field is not empty, the PHP exits.  In addition, I'd like to discard forms which are filled out in a few seconds, and that's the purpose of this question.
0
 

Author Comment

by:ddantes
Comment Utility
Gary:  our postings crossed.  Thanks for your comment.   I'm inexperienced at this, so I'll need to know where that code is placed -- in the contact form, or in the form-handling PHP?   If the cookie is set, then what code is required to distinguish if a the submission was automated instead of human?
0
 
LVL 58

Accepted Solution

by:
Gary earned 350 total points
Comment Utility
Actually that is backwards with my cookie example - you want a long timeout and then check it exists but...
Why not use the session idea I posted in one of your previous questions - it's basically the same as the cookie idea
On the contact form set a session, on the submission page check the session exists - it will only exist if someone actually went to the contact form.
Bit more simpler than cookies
So on the contact form you have (at the top of the page)
<?php
session_start();
$_SESSION["contact_form"]=1;


One the form submission page you have
<?php
session_start();
if(isset($_SESSION['"contact_form"])){
// Contact form was actually visited
}
0
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 75 total points
Comment Utility
You don't actually need a cookie.  Just put the timestamp into the PHP session.  But I agree with @gr8gonzo.  The more you do to secure your form, the more likely you will secure it against someone whose input you want.

<?php // demp/temp_ddantes.php
error_reporting(E_ALL);

// SAVE THE FORM CREATION TIME IN THE SESSION
session_start();
if (empty($_SESSION['time'])) $_SESSION['time'] = time();

// IF THE FORM IS SUBMITTED
if (!empty($_POST))
{
    // SHOW THE TIME IT TOOK TO SUBMIT THE FORM
    $lapse = time() - $_SESSION['time'];
    echo PHP_EOL . "YOU SUBMITTED THE FORM IN $lapse SECONDS";
    $_SESSION['time'] = NULL;
    die();
}

// CREATE THE FORM WITH HEREDOC NOTATION
$form = <<<ENDFORM
<form method="post">
<input type="submit" name="submit_button" value="go" />
</form>
ENDFORM;

echo $form;

Open in new window

0
 
LVL 82

Expert Comment

by:Dave Baldwin
Comment Utility
I think the timeout is a waste of time.
0
 
LVL 34

Assisted Solution

by:gr8gonzo
gr8gonzo earned 75 total points
Comment Utility
I would suggest doing something like this:

<?php
class FormToken
{
   private static $key = "s3cr3t!";

   // Generate some HTML to be inserted into a <form>
   public static function Generate()
   {
     $ts = time();
     $token = md5(FormToken::$key.$ts);
     @$_SESSION["tokens"][$ts] = $token;
     $html = "<input type='hidden' name='form_token_ts' value='{$ts}'><input type='hidden' id='form_token' name='form_token' value=''<script type='text/javascript'>document.getElementById('form_token').value = '{$token}';</script>";
     return $html;
   }

   public static function Validate()
   {
      global $_POST;

      // Make sure you have the fields
      if(!isset($_POST["form_token"])) { return false; }
      if(!isset($_POST["form_token_ts"])) { return false; }

      // Make sure the token exists and is valid
      if(!isset($_SESSION["tokens"][$_POST["form_token_ts"]])) { return false; }
      if($_SESSION["tokens"][$_POST["form_token_ts"]] != $_POST["form_token"]) { return false; }

      // Only allow a token to be used once
      unset($_SESSION["tokens"][$_POST["form_token_ts"]]);

      return true;
   }   
}

if(!FormToken::Validate())
{
    echo "Invalid form token!";
    die();
}

Open in new window


...then inside your form, just add:

echo FormToken::Generate();

Open in new window


For example:

<form action='blah blah' method='post'>
First Name: ...
blah blah...

<?php
echo FormToken::Generate();
?>

<input type='submit'>
</form>

Open in new window

0
 

Author Comment

by:ddantes
Comment Utility
I appreciate everyone's guidance very much .  However, when Experts have differing opinions, it's challenging for an amateur to sort it out.  I was an E.R. doc for 27 years, and I can only begin to imagine the frustration of a patient's family, when consulting physicians offered contradictory diagnoses, or recommendations, in an urgent situation.  Fortunately, if I make a mistake in my coding, all I need to do is retype something!

In any case, since I need to keep things simple, I'd like to use Gary's method of checking if a session exists.  Gary: after the session is set at the top of the contact form, do I need anything to close the php code, before html starts?   Also, on the PHP page, do I need an "else" code, to exit if the session wasn't set?
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Just
?>

Sometimes in real life I think click Undo!
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Re the else - no you could just let the page die since it's going to be a bot.
0
 

Author Comment

by:ddantes
Comment Utility
Got it.  Thanks to everyone!
0
 

Author Comment

by:ddantes
Comment Utility
Gary:  When I add

<?php
 session_start();
 if(isset($_SESSION['"contact_form"])){
 // Contact form was actually visited
 }

to the top of my PHP page, it seems to affect all the rest of the code, perhaps invalidating it.  I think I am missing some required syntax.  The pre-existing PHP code snippet is posted with the original question.

Also, when I add the php code at the top of my contact page, my html editor tells me I must save the page with a .php extension.  I wasn't expecting that.
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Post the page as you have it now - you probably have another opening php tag

For the contact page it will need to be .php or you will need to add an handler in your.htaccess file.
Or how likely is it someone will just go direct to your contact page?
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 

Author Comment

by:ddantes
Comment Utility
Thank you.   For the contact page, there are links to it throughout my website. I'd prefer not to change all those links, and the file extension, so I'll add an handler to .htaccess.    I've posted the problematic page below...


<?php 
session_start();
 if(isset($_SESSION['"contact_form"])){
 // Contact form was actually visited
 } 
 
if($_POST['name'] != '') {  //show thankyou page and exit.
    header("Location: thankyou.htm"); /* Redirect to thankyou page */
    exit; }
$errors = '';
$myemail = 'stay@mauitradewinds.com';//<-----Put Your email address here.
if(empty($_POST['firstname'])  || 
   empty($_POST['lastname'])  ||
   empty($_POST['email']) || 
   empty($_POST['message']))
{
    $errors .= "\n Error: At a minimum, we need your name, Email address and message in order to transmit your form.";
}
else
{
    $firstname = array_key_exists('firstname',$_POST) ? $_POST['firstname']:''; 
    $lastname = array_key_exists('lastname',$_POST) ? $_POST['lastname']:''; 
    $Address = array_key_exists('Address',$_POST) ? $_POST['Address']:''; 
    $City = array_key_exists('City',$_POST) ? $_POST['City']:''; 
    $State = array_key_exists('State',$_POST) ? $_POST['State']:''; 
    $Zip = array_key_exists('Zip',$_POST) ? $_POST['Zip']:''; 
    $Country = array_key_exists('Country',$_POST) ? $_POST['Country']:''; 
    $Phone = array_key_exists('Phone',$_POST) ? $_POST['Phone']:''; 
    $email_address = array_key_exists('email',$_POST) ? $_POST['email']:''; 
    $message = array_key_exists('message',$_POST) ? $_POST['message']:''; 
    if (!preg_match(
    '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i', 
    $email_address))
    {
        $errors .= "\n Error: Invalid email address";
    }
}

if( empty($errors))
{
	$to = $myemail; 
	$email_subject = "Contact form";
	$email_body = "You have received a contact form from Site-1. ".
	" Here are the details:\n First Name: $firstname \n Last Name: $lastname \n Address: $Address \n City: $City \n State: $State \n Zip: $Zip \n Country: $Country \n Phone: $Phone \n Email: $email_address \n Message: $message"; 
	
	$headers = "From: $myemail\n"; 
	$headers .= "Reply-To: $email_address";
	
	mail($to,$email_subject,$email_body,$headers);
	//redirect to the 'thank you' page
	header('Location: thankyou.htm');
	exit;//You should always "exit" immediately after a redirection request
} 
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>
<head>
	<title>Contact form handler</title>
</head>

<body>
<!-- This page is displayed only if there is some error -->
<?php
echo nl2br($errors);
?>


</body>
</html>

Open in new window

0
 
LVL 34

Expert Comment

by:gr8gonzo
Comment Utility
I understand what you're saying about differing opinions and it can be confusing. Part of the issue is that some of these cross-opinions are also discussion between experts, too. So it's more like being in the room with 5 doctors that are all trying to talk at once, but the doctors are also trying to figure out the most effective method.

That said, spam bots are far more likely to accept cookies nowadays than before. A lot of the underlying engines for spam bots all automatically handle cookies (e.g. it's two lines of code for cURL-based bots), so I wouldn't try to judge whether someone is a bot or not based on the existence of a cookie. That might have been effective several years ago, but very far less effective today. Imagine you create a robot that is designed to stand in front of the post office window and hand them post card after post card, all day long.

So the post office says that before anyone can send mail, they must now say the phrase, "I like cookies." At first, the robot fails, but in about 15 minutes, the robot's creator puts in a new feature that lets the robot say that phrase, and all other robot creators in the world start putting in the same feature. Over a few years, it becomes commonplace, so the effectiveness of that requirement is pretty much zapped.

The Javascript approach is a lot more difficult for bots to handle because Javascript itself is its own programming engine and is often tied in pretty closely to the user interface, and it's somewhat hefty to load, which can slow down spam bots (which reduces their overall economic effectiveness). A spam bot would have to load up the engine and process each page completely, including Javascript, in order to submit the form. Now imagine the post office required each person to explain the meaning of a random poem before each attempt to send a postcard. The robot would have to be programmed to understand poetry and meaning, which is not a simple task. It's not just a simple calculation anymore. At that point, whoever is programming the robot would likely just move on to a different post office that didn't have that requirement. This requirement of "understanding" or interpretation is where Javascript comes in and makes it much harder to implement.

The CAPTCHA approach works in a very similar way. It requires humans to see and type in the words or letters in a picture, which requires some level of understanding.

If these two approaches weren't so effective, you wouldn't have huge sites like Ticketmaster and Google that use them to prevent bots and other forms of automation.
0
 

Author Comment

by:ddantes
Comment Utility
That was helpful!
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
I had an extra apostrophe in their, but anyway change that code to this

<?php 
session_start();
 if(!isset($_SESSION["contact_form"])){
 exit;
 } 

Open in new window

0
 

Author Comment

by:ddantes
Comment Utility
That fixed it.
One more question, if I may?  I'm thinking of adding this code to .htaccess...
AddHandler application/x-httpd-php52 .php .htm .html

I'm wondering if that will slow down everything, causing all my pages to be processed in a way which uses more resources?
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Yes by about a nanosecond
0
 

Author Comment

by:ddantes
Comment Utility
I can live with that.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
Regarding this bit of code:

if(isset($_SESSION['"contact_form"])){
 // Contact form was actually visited
 }

The comment may be exactly right, but it may also hide a deficiency in the thought processes needed to frustrate 'bots.  You would want to clear $_SESSION['contact_form'] each time you processed a form.  Otherwise the 'bot can visit your page once, which will cause the session variable to be set.  Then the 'bot can fire attack vectors into your action script, knowing that once the session variable was set, it was not cleared.  These may seem like little things, and they are, right up to the moment that you're under attack.

Also, some form token approaches puts data into the HTML form that will be read by the 'bot and submitted along with the rest of the form.  I didn't look too closely at that suggestion here, but unless the token is loaded with an AJAX request, it may not be as effective as CAPTCHA.

The best answer is usually some kind of CAPTCHA, except not reCaptcha any more.
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
The session isn't set unless the contact page is visited so there is no session to clear
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
The session isn't set unless the contact page is visited so there is no session to clear
Gary: Here's my concern.

1. 'Bot reads the contact page.  This causes the session variable to get set.
2. 'Bot submits the form.  Session variable has been set and unless cleared, it remains set.
3. 'Bot submits the form over and over again,  Each submission finds the session variable is still there.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
AddHandler application/x-httpd-php52 .php .htm .html
Not sure if I'm on firm ground in this assumption, but if php52 in the AddHandler statement means you're running PHP Version 5.2 you should upgrade at once.  PHP 5.2 is no longer supported for anything at all, not even for security fixes.  The current levels of PHP are documented on the PHP.net home page, righthand side.  You probably want to be at PHP 5.4.30 or higher.
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
Unlikely a bot will visit the contact page and then post it's own data seperately to the submission page
Of course you could then check the referrer as well but again not 100% guaranteed
Nothing is going to be foolproof but with all the other measures ddantes is taking they are at the point where there is nothing else to be done bar removing the site from the internet
0
 

Author Comment

by:ddantes
Comment Utility
It looks like version 5.3.3-7 is running on the server.
0
 

Author Comment

by:ddantes
Comment Utility
Gary: Now that the contact form and form handler are configured to exit if a session doesn't exist, is there a way I can simulate the submission of a form without a session, to see what happens?   What about removing the session code from the top of the contact page and submitting it?
0
 
LVL 58

Expert Comment

by:Gary
Comment Utility
After session_start() add

unset($_SESSION["contact_form"]);

Or just remove the session from the contact form so it's never set
0
 

Author Comment

by:ddantes
Comment Utility
Thanks!
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

This article will explain how to display the first page of your Microsoft Word documents (e.g. .doc, .docx, etc...) as images in a web page programatically. I have scoured the web on a way to do this unsuccessfully. The goal is to produce something …
Generating table dynamically is the most common issue faced by php developers.... So it seems there is a need of an article that explains the basic concept of generating tables dynamically. It just requires a basic knowledge of html and little maths…
The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now