Link to home
Start Free TrialLog in
Avatar of ddantes
ddantesFlag for United States of America

asked on

php-generated message

I'm testing a very simple captcha on my website's contact form, because the form is being abused by spambots.  I'm at the beginning of the learning curve with php, and plan to refine this captcha in the future.  Meanwhile, there is a specific issue I'd like to resolve...   The php code generates messages which prompt the visitor to complete required form fields, and all but one of these messages appear as popups, without opening a new browser window.   However, a message informing the visitor of a captcha error opens a new browser window.  When that happens, the visitor needs to use the back button, and repopulate the contact form.  Some browsers preserve the form field data, but not Internet Explorer.  I don't know why that particular message behaves this way, but I'd prefer the message appear as a popup, like the other messages.  For example, if the Email field doesn't contain a valid Email address, there is a message prompting the visitor to correct it.  Please advise how to adjust my code so that a captcha error can be corrected without leaving the contact form page.  The contact page is at www.mauitradewinds.com/contact.htm  and the php code snippet is attached.


<?php 
$errors = '';
$myemail = 'stay@mauitradewinds.com';//<-----Put Your email address here.
if(empty($_POST['firstname'])  || 
   empty($_POST['lastname'])  ||
   empty($_POST['email']) || 
   empty($_POST['captcha']) || 
   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']:''; 
    $email_address = array_key_exists('email',$_POST) ? $_POST['email']:''; 
    $message = array_key_exists('message',$_POST) ? $_POST['message']:''; 
    $captcha = array_key_exists('captcha',$_POST) ? $_POST['captcha'] : FALSE;
    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 (!preg_match(
     "/Maui/i",
     $captcha))
    {
        $errors .= "\n Error: Please check your spelling and try again";
    }
}

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 Email: $email_address \n Message: \n $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

Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Please advise how to adjust my code so that a captcha error can be corrected without leaving the contact form page.
Sorry, it can't be done.  CAPTCHA does not work that way.

The whole point of CAPTCHA is to give us a server-side test that can be run after the form has been submitted.  It is only run on the server (there is no JavaScript involved).  It is tested after the form has been submitted and no other changes can be made to the data.  CATPCHA, if successful, verifies that a human entered the information in the form.  And the CAPTCHA, if it fails, fails after the form has been submitted and the original form script is no longer running.

Think about it for a moment... If the client could say, "the CAPTCHA is OK" what would be the point of testing the CAPTCHA at all?  The CAPTCHA is to test the client's input for "humanness."  So it wouldn't make any sense to rely on the client to tell the server it was a human -- that would invite automated form submissions from a 'bot that lied about being a human!

You can make the whole CAPTCHA process suck less if you follow this general design

1. In the form script, try to use elements in $_SESSION to populate the HTML form input controls.  If they are there, use them.  If they are not there, use blanks
2. In the action script (after data validation and sanitization) copy the data from $_POST into $_SESSION.
3. If the CAPTCHA fails, tell the client and give a link back to the form script.

An alternative to using the session is to use a single script file for both the form and the action script (example below).

By doing that you can preserve the posted data so the client does not have to retype all of the information in the form.

Here is the design pattern that shows how to preserve the good information, show error messages about the bad information.  It does not have a CAPTCHA, but I think you can get the idea.

<?php // demo/form_highlight_errors.php
error_reporting(E_ALL);

/**
 * DEMONSTRATE HOW TO HIGHLIGHT ERRORS IN FORM INPUT, AND REMEMBER VALID INPUTS
 * CLIENT IS ASKED TO PUT IN A VALUE
 * IF THE VALUE FAILS OUR TEST WE SHOW AN ERROR MESSAGE
 * WE PUT A MARKER NEXT TO THE INPUT CONTROL ON THE FORM
 * WE TURN THE FORM BORDER RED
 * SEE http://www.w3schools.com/CSS/pr_class_visibility.asp
 */


// THESE STYLE ELEMENTS ARE SET FOR THE SCRIPT INITIALIZATION
$error_abc = $error_xyz = $error_any = 'hidden';
$boxer_abc = $boxer_xyz              = 'black';


// CAPTURE AND NORMALIZE THE POST VARIABLES - ADD YOUR OWN SANITY CHECKS HERE
$abc = (isset($_POST["abc"])) ? trim(strtoupper($_POST["abc"])) : NULL;
$xyz = (isset($_POST["xyz"])) ? trim(strtoupper($_POST["xyz"])) : NULL;


// IF ANYTHING WAS POSTED, VALIDATE IT
if (!empty($_POST))
{
    // VALIDATE THE 'abc' FIELD
    if ($abc != 'ABC')
    {
        // VALIDATION FAILED
        $error_abc = $error_any = 'visible';
        $boxer_abc = 'red';

        // BECAUSE THIS FAILED VALIDATION, REMOVE IT FROM THE FORM
        $abc       = NULL;
    }

    // VALIDATE THE 'xyz' FIELD
    if ($xyz != 'XYZ')
    {
        // VALIDATION FAILED
        $error_xyz = $error_any = 'visible';
        $boxer_xyz = 'red';

        // BECAUSE THIS FAILED VALIDATION, REMOVE IT FROM THE FORM
        $xyz       = NULL;
    }

    // DO WE HAVE INPUT FREE FROM ANY ERRORS?
    if ($error_any != 'visible')
    {
        echo "CONGRATULATIONS";
        die();
    }

    // OTHERWISE... OOPS - WE HAVE ERRORS AND MUST SHOW THE FORM AGAIN
}

// IF NOTHING WAS POSTED, OR IF THERE ARE ERRORS, WE NEED NEW CLIENT INPUT
$form = <<<ENDFORM
<style type="text/css" media="all">
.error_any { visibility:$error_any; }
.error_abc { visibility:$error_abc; }
.error_xyz { visibility:$error_xyz; }
</style>
<pre>
<form method="post">
<span class="error_any">PLEASE CORRECT THE FOLLOWING ERRORS</span>
<span class="error_abc">YOU MUST ENTER 'abc' IN THIS FIELD</span>
PLEASE ENTER "ABC" HERE: <input style="border-color:$boxer_abc;" name="abc" value="$abc" />
<span class="error_xyz">YOU MUST ENTER 'xyz' IN THIS FIELD</span>
PLEASE ENTER "XYZ" HERE: <input style="border-color:$boxer_xyz;" name="xyz" value="$xyz" />
<input type="submit" />
</form>
ENDFORM;

// WRITE THE FORM WITH THE APPROPRIATE CSS STYLES ON THE ERROR MESSAGE FIELDS
echo $form;

Open in new window

Avatar of ddantes

ASKER

Thank you.  It will take me a while to explore your recommendations, and I appreciate the sample code.   However, when you say "it can't be done", perhaps I failed to articulate my question clearly.  When I visit a page with a captcha, and I mistakenly enter an erroneous character, there is a popup on that very page, telling me I failed the test.  I can correct my mistake without reloading the page and starting the entire process over.  I'm asking how to create that behavior for a captcha error on my contact form, instead of navigating away from the form to display an error on a new page.   If you try the form, and enter an invalid Email address, you will see a popup asking you to correct that.  I'd like that to happen when there is a captcha error.

I believe the key to this is in the contact.htm  html, toward the bottom of the page, in the javascript relating to form validation.  The code "frmvalidator.addValidation("captcha","captcha","Please check your spelling and try again");"  is not having any effect.
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ddantes

ASKER

That was very helpful.  Before closing the question, may I ask your opinion about adding this code to the javascript validation section of the html page?

frmvalidator.addValidation("captcha","regexp=Maui","Please check your spelling and try again");

Of course, that is providing the answer to the captcha.  However, should I be concerned that a spam bot will be able to harvest that from the html and insert it in the captcha field?
I don't really know for sure.  I'm not very good with JavaScript and it would be a research project for me.  My general sense is that you can hide and pass information in the PHP session and this information will be invisible to 'bots.  So I would lean in that direction rather than putting the answer into the HTML document in clear text.  There are many levels of CAPTCHA in the article and none of them would put the answer into the HTML document.
Avatar of ddantes

ASKER

Thank you for all your input!
Thanks for using E-E and best of luck with your project, ~Ray