Solved

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

Posted on 2014-07-21
32
443 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 13
  • 10
  • 4
  • +3
32 Comments
 
LVL 21

Expert Comment

by:Randy Poole
ID: 40209786
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
ID: 40209816
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 35

Expert Comment

by:gr8gonzo
ID: 40209837
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
SharePoint Admin?

Enable Your Employees To Focus On The Core With Intuitive Onscreen Guidance That is With You At The Moment of Need.

 
LVL 58

Expert Comment

by:Gary
ID: 40209842
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
ID: 40209846
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
ID: 40209854
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
ID: 40209865
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 110

Assisted Solution

by:Ray Paseur
Ray Paseur earned 75 total points
ID: 40209885
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 83

Expert Comment

by:Dave Baldwin
ID: 40209912
I think the timeout is a waste of time.
0
 
LVL 35

Assisted Solution

by:gr8gonzo
gr8gonzo earned 75 total points
ID: 40209957
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
ID: 40209960
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
ID: 40209974
Just
?>

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

Expert Comment

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

Author Comment

by:ddantes
ID: 40209990
Got it.  Thanks to everyone!
0
 

Author Comment

by:ddantes
ID: 40210022
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
ID: 40210054
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
 

Author Comment

by:ddantes
ID: 40210082
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 35

Expert Comment

by:gr8gonzo
ID: 40210087
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
ID: 40210092
That was helpful!
0
 
LVL 58

Expert Comment

by:Gary
ID: 40210111
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
ID: 40210138
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
ID: 40210150
Yes by about a nanosecond
0
 

Author Comment

by:ddantes
ID: 40210166
I can live with that.
0
 
LVL 110

Expert Comment

by:Ray Paseur
ID: 40210213
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
ID: 40210224
The session isn't set unless the contact page is visited so there is no session to clear
0
 
LVL 110

Expert Comment

by:Ray Paseur
ID: 40210241
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 110

Expert Comment

by:Ray Paseur
ID: 40210244
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
ID: 40210247
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
ID: 40210253
It looks like version 5.3.3-7 is running on the server.
0
 

Author Comment

by:ddantes
ID: 40212279
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
ID: 40212284
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
ID: 40212371
Thanks!
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Foreword (July, 2015) Since I first wrote this article, years ago, a great many more people have begun using the internet.  They are coming online from every part of the globe, learning, reading, shopping and spending money at an ever-increasing ra…
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

728 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