Jump to content

Archived

This topic is now archived and is closed to further replies.

Ramjam

Contact form with math captcha

Recommended Posts

Hi everyone

 

I've successfully created a contact form with php that gives the various required messages.  :o  Now I would like to add a simple random arithmetic captcha (non-image).  See the anonymised (working) html form and existing (working, but without arithmetic captcha) php below.

 

The idea is to show "Incorrect answer" in the same way as the other error messages, or to pass to the Thankyou page for a correctly filled out form with correct answer.  I've had a good go at this but can't quite get it to work.  Any assistance much appreciated.

 

___

 

HTML:

<div id="form" class="form">
<h3 class="title">Contact Form</h3>
<p>Required fields are in <strong>bold</strong></p>

<form action="contact.php" method="post" target="_blank">
<p><strong>Name:</strong><br /><input type="text" name="yourname" size="35" /></p>
<p><strong>Email address:</strong><br /><input type="text" name="email" size="35"/></p>
<p>Telephone number:<br /><input type="text" name="telephone" size="35"/></p>
<p>Website (if applicable):<br /><input type="text" name="website" size="35"/></p>

<p>Area of interest:
<input type="radio" name="likeit" value="A" checked="checked" /> A
<input type="radio" name="likeit" value="B" /> B
<input type="radio" name="likeit" value="C" /> C</p>

<p>How did you hear about us?
<select name="how">
<option value=""> -- Please select -- </option>
<option>Recommendation</option>
<option>Internet</option>
<option>Advertisement</option>
<option>Other</option>
</select></p>

<p><strong>Your message subject:</strong><br /><input type="text" name="subject" size="35"/></p>

<p><strong>Your message:</strong><br />
<textarea name="comments" rows="10" cols="40"></textarea></p>

<p>Please answer the following arithmetic question: What is <?php echo $digit1;?> + <?php echo $digit2;?>?
<input name="captcha" type="text" size="2" id="captcha"/></p>


<p><input type="submit" value="Send" /></p>

</form>

 

PHP:

<?php
/* Contact form with arithmetic captcha */
$myemail  = "enquiries@X.co.uk";

/* Check all form inputs using check_input function */
$yourname = check_input($_POST['yourname'], "Enter your name");
$email    = check_input($_POST['email']);
$telephone  = check_input($_POST['telephone']);
$website  = check_input($_POST['website']);
$likeit   = check_input($_POST['likeit']);
$how_find = check_input($_POST['how']);
$subject  = check_input($_POST['subject'], "Add a subject");
$comments = check_input($_POST['comments'], "Add your message");

/* If e-mail is not valid show error message */
if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/", $email))
{
show_error("Email address is not valid");
}

/* If URL is not valid set $website to empty */
if (!preg_match("/^(https?:\/\/+[\w\-]+\.[\w\-]+)/i", $website))
{
    $website = '';
}

/* Message for the email */
$message = "Hello!

Your contact form has been submitted by:

Name: $yourname
Email: $email
Telephone: $telephone
URL: $website

Area of interest? $likeit
How did they find us? $how_find

Comments:
$comments

End of message
";

/* Send the message using mail() function */
mail($myemail, $subject, $message);

/* Redirect visitor to the thankyou page */
header('Location: thankyou.html');
exit();

/* Functions used */
function check_input($data, $problem='')
{
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    if ($problem && strlen($data) == 0)
    {
        show_error($problem);
    }
    return $data;
}

function show_error($myError)
{
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
Head data in here
-->
    <html xmlns="http://www.w3.org/1999/xhtml">

    <body>

<div id="mainheader">

<div id="mainlogo">

<h1><a href="http://www.X.co.uk/" title="X">
			<img style="border:0;width: 260px; height: 160px;" src="images/X.jpg" alt="X" /></a></h1>
</div>
</div>

<div id="content">
<div class="content">
	<h2 class="title">Error!</h2>

<p><strong>Please correct the following error:</strong></p>
<p><?php echo $myError; ?></p>

</div>
</div>

    <div id="panel">

        <div id="main" class="boxed">
            <h2 class="heading">Main</h2>
            <ul>
                <li><a href="index.html">Home</a> </li>
                <li><a href="about.html">About</a> </li>
                <li><a href="contact.html">Contact</a> </li>
            </ul>
        </div>

        <div id="services" class="boxed">
            <h2 class="heading">Services</h2>
            <ul>
                <li><a href="services.html">Services</a> </li>
                <li><a href="recent-projects.html">Recent projects</a> </li>
            </ul>
        </div>

        <div id="pricing" class="boxed">
            <h2 class="heading">Pricing</h2>
            <ul>
                <li><a href="pricing.html">Pricing</a> </li>
            </ul>
        </div>

        <div id="info" class="boxed">
            <h2 class="heading">Info</h2>
            <ul>
                <li><a href="tips-and-tricks.html">Tips and tricks</a> </li>
                <li><a href="useful-links.html">Useful links</a> </li>
                <li><a href="faq.html">Frequently asked questions</a> </li>
                <li><a href="site-map.html">Site map</a> </li>
            </ul>
        </div>

	<div id="contact" class="boxed">
		<h2 class="heading">Contact</h2>
		<ul>
		<li><a href= "mailto:enquiries&#38;#64;X.co.uk">Contact by email</a> </li>
		<li><strong>Telephone:<br />X</strong> </li>
		</ul>
	</div>

    </div>

   <div id="mainfooter">
	<p> &#38;#169; 2011 X<br />Designed by <a href="http://www.X.co.uk/" title="X"><strong>X</strong></a> </p>
		<a href="http://validator.w3.org/check?uri=referer" title="Valid XHTML 1.0">
                <img style="border:0;width:88px;height:31px" src="images/valid-xhtml10.png" alt="Valid XHTML 1.0" />
            </a>
		<a href="http://jigsaw.w3.org/css-validator/check/referer" title="Valid CSS!">
                <img style="border:0;width:88px;height:31px" src="images/vcss.gif" alt="Valid CSS!" />
            </a>
    </div>

    </body>
    </html>
<?php
exit();
}
?>

Share this post


Link to post
Share on other sites

Generally, the idea of captcha is to prevent automated form processing. Any non-image comparisons will be easily solved.

 

Regardless, I would use sessions to solve this issue.

 

Simply store the expected result in a session variable on the first page, and make sure it matches on the second

 

page1.php:

<?php

session_start();

$digit1 = mt_rand(1,20);
$digit2 = mt_rand(1,20);
if( mt_rand(0,1) === 1 ) {
$math = "$digit1 + $digit2";
$_SESSION['answer'] = $digit1 + $digit2;
} else {
$math = "$digit1 - $digit2";
$_SESSION['answer'] = $digit1 - $digit2;
}

?>

<form method="POST" action="page2.php">
What's <?php echo $math; ?> = <input name="answer" type="text" /><br />
<input type="submit" />
</form>

 

page2.php

<?php 

session_start();

echo "You entered ".htmlentities($_POST['answer'])." which is ";

if ($_SESSION['answer'] == $_POST['answer'] )
echo 'correct';
else
echo 'wrong. We expected '.$_SESSION['answer'];

?>

Share this post


Link to post
Share on other sites

To xyph:

 

Thank you - everything is working perfectly!  I compliment you on your elegant and concise code; exactly what I was hoping for.  Your clear explanation helped me to see where I had been going wrong & you have saved me a page of code; my own clumsy solution had 3 pages whereas yours only needs 2.  8)

 

I agree with your observation re non-image code, but many of my site visitors are unable to decipher visual captchas & I reckon this dynamic maths captcha will surely confound my former spammers.

 

Mods - case closed! - please mark this one as Solved.

 

Excellent forum.  All the best.

Share this post


Link to post
Share on other sites
I reckon this dynamic maths captcha will surely confound my former spammers.

 

Unlikely, as it only takes a few lines of php code to automate the solving of your text based math problem, i.e. scrape the string out of the form page, solve the math using an eval() statement, submit to your form processing page.

 

However, if you output the whole math question ON an image, and use randomly worded questions with random values in them, it will become much more difficult for an automated script to solve this as it must first do OCR to even read the math question.

 

Typical image captchas have 4-8 characters or a word or two to read and input. Doing the OCR to read a smaller number of characters is going to be more accurate than reading a larger number of characters, symbols, and numbers. If you output the whole math question with several words, numbers, and number names in it, just getting the OCR to accurately return all the words to even parse by a script will thwart all but a dedicated spammer.

 

Also, you need to clear the $_SESSION variable after you successfully test it in the form processing code so that someone cannot enter the correct answer once and repeatedly submit to the form processing code and you need to check if the $_SESSION variable is NOT empty, because an empty session variable will be equal to an empty post variable every time.

Share this post


Link to post
Share on other sites

PFMaBiSmAd is correct as usual.

 

I didn't include checks in my script, nor warn you about them in my post.

 

Definitely make sure the session variable is defined, and clear it after the comparison.

 

if ( isset($_SESSION['answer']) && $_SESSION['answer'] == $_POST['answer'] )
echo 'correct';
else
echo 'wrong. We expected '.$_SESSION['answer'];
unset($_SESSION['answer']);

Share this post


Link to post
Share on other sites

xyph, PFMaBiSmAd - Thanks to you both for the additional caveats.  I'm on the case!

 

All the best & keep up the good work.

Share this post


Link to post
Share on other sites

Hello again everyone.

 

Update: The form is working nicely, and I've even had some incoming enquiries! :D  The Session checks are doing their job well.

 

A final tweak: I've currently got a few effective rules to sanitise the input and throw up appropriate error messages such as "Add your message title" or "Invalid email format", with the math captcha result message being generated independently.  So the effect is:

 

Once the form has been filled out correctly, the captcha result (either 'correct' or 'incorrect') is displayed as discussed. That's fine, but is there a simple way to make the captcha itself a rule so that it thows up an error message similarly to the other rules?  Eg the email rule:

 

/* Check all form inputs using check_input function */

...

$email    = check_input($_POST['email']);

...

/* If e-mail is not valid show error message */
if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/", $email))
{
show_error("Email address is not valid.");
}

...

/* Functions used */
function check_input($data, $problem='')
{
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    if ($problem && strlen($data) == 0)
    {
        show_error($problem);
    }
    return $data;
}

function show_error($myError)

Share this post


Link to post
Share on other sites

if ( !isset($_SESSION['answer']) || $_SESSION['answer'] != $_POST['answer'] )

{

show_error("Math Captcha Incorrect.");

}

Share this post


Link to post
Share on other sites

Hi, I am using the script as posted by xyph. One moment the script is working absolutely fine although after a few hours of being uploaded to my webserver, on submitting the form, it says "Math Captcha Incorrect" even though it isn't incorrect. Can anyone explain why this might be occuring? I did have to modify the code slightly so that it follows through with my PHP form submission setup.

 

The only way to get it working properly again is to delete the files from the webserver and reupload them. This is obviously causing users an inconvenience when they submit data through one of the forms, not just myself!

 

<?php
session_start();

if (!isset($_SESSION['answer']) || $_SESSION['answer'] != $_POST['answer'] ) { exit("<b><center>Math Captcha Incorrect. Please <a href='Javascript:history.go(-1)'>Go Back</a> and try again</center></b>"); }
if (!$_POST['email']) { exit("<b><center>You did not supply an E-Mail address. We need this to reply to your E-Mails.<br /><br /><a href='Javascript:history.go(-1)'> < Go Back</a></center></b>"); }

 

 

Thanks in advance!

Share this post


Link to post
Share on other sites

Hi, I am using the script as posted by xyph. One moment the script is working absolutely fine although after a few hours of being uploaded to my webserver, on submitting the form, it says "Math Captcha Incorrect" even though it isn't incorrect. Can anyone explain why this might be occuring? I did have to modify the code slightly so that it follows through with my PHP form submission setup.

 

The only way to get it working properly again is to delete the files from the webserver and reupload them. This is obviously causing users an inconvenience when they submit data through one of the forms, not just myself!

 

session_start();

if (!isset($_SESSION['answer']) || $_SESSION['answer'] != $_POST['answer'] ) { exit("<b><center>Math Captcha Incorrect. Please <a href='Javascript:history.go(-1)'>Go Back</a> and try again</center></b>"); }
if (!$_POST['email']) { exit("<b><center>You did not supply an E-Mail address. We need this to reply to your E-Mails.<br /><br /><a href='Javascript:history.go(-1)'> < Go Back</a></center></b>"); }

 

Thanks in advance!

 

People hate CAPTCHA's.  They are annoying and slow down a process.  On top of that, captcha's can be bypassed now by spam-bots.

 

For beating bots, you can try some simple, yet highly effective, tactics.  First, you need to understand the mindset of a bot:

 

1. Its all about spamming as many forms on as many sites as quickly as possible.  The bot hits your site, locates any/all forms, and goes to work autopopulating any/all fields it can find.  All of this within seconds (at most; and depending on how fast your HTML is rendered).

 

Keeping that in mind, using a timer within the page/form to detect how quickly your form was submitted from time of page load is highly effective (actually, since I have implemented this tactic on several of my sites, all spam activity has been eliminated 100%).

 

session_start();

$_SESSION['start_time'] = time();

if (isset($_POST['submit'])) {

if (ctype_digit($_POST['start_time'])) {

	$now = time();
	$seconds = 5; // this is the number of minimum seconds that must pass before the user can submit the form; change to whatever value is appropriate for your form, ie. if it takes approx 60 seconds to fill out your form, then increase this number accordingly.  Remember that a bot will fill out the form as quickly as possible (talking near instantly)

	if (($_POST['start_time'] - $now) < $seconds) {
		// form submitted too quickly; do not allow processing
	}
	else {
		// OK; continue with processing
	}
}
else {
	// bot changed value of 'start_time'; stop processing
}
}
<form action="" method="post">
<input type="hidden" name="start_time" value="<?php echo $_SESSION['start_time']; ?>"/>
...
</form>

 

2. Bots will typically attempt to fill out any and all form inputs/textareas, etc.  You can place a hidden <textarea name="info" style="display:none"></textarea> within your form to attract bots.  Then, within your processing code, check to see if there is a value within this textarea.  Since it is hidden to the user, there should be no value.  If there is, then you know it was a bot that entered that value.

 

Now, there are certain e-readers and such that might display this hidden field (devices for the visually impaired that will read all inputs out to the user), so you will want to ensure they don't get caught in a loop hole with text advising them not to enter any values within that box should it be made available to them.

Share this post


Link to post
Share on other sites

Noticed typo in my code above...

 

This line:

 

if (($_POST['start_time'] - $now) < $seconds) {

 

Should be:

 

if (($now - $_POST['start_time']) < $seconds) {

 

And:

 

if (isset($_POST['submit'])) {

 

Is relying on a submit button/input field with the name "submit".

Share this post


Link to post
Share on other sites

Hi

 

Sorry to drag this topic up again, but a question regarding the below solution (which is quite a neat little trick!), however how does this work on the rare occasion when the clocks go forward / backwards?

 

How does the below solution handle this, surely it will fail..... how can we get around this as I would really like to use this solution.

 

Thanks

 

 

 

session_start();

$_SESSION['start_time'] = time();

if (isset($_POST['submit'])) {

	if (ctype_digit($_POST['start_time'])) {

		$now = time();
		$seconds = 5; // this is the number of minimum seconds that must pass before the user can submit the form; change to whatever value is appropriate for your form, ie. if it takes approx 60 seconds to fill out your form, then increase this number accordingly.  Remember that a bot will fill out the form as quickly as possible (talking near instantly)

		if (($_POST['start_time'] - $now) < $seconds) {
			// form submitted too quickly; do not allow processing
		}
		else {
			// OK; continue with processing
		}
	}
	else {
		// bot changed value of 'start_time'; stop processing
	}
}
<form action="" method="post">
	<input type="hidden" name="start_time" value="<?php echo $_SESSION['start_time']; ?>"/>
	...
</form>

2. Bots will typically attempt to fill out any and all form inputs/textareas, etc.  You can place a hidden <textarea name="info" style="display:none"></textarea> within your form to attract bots.  Then, within your processing code, check to see if there is a value within this textarea.  Since it is hidden to the user, there should be no value.  If there is, then you know it was a bot that entered that value.

Now, there are certain e-readers and such that might display this hidden field (devices for the visually impaired that will read all inputs out to the user), so you will want to ensure they don't get caught in a loop hole with text advising them not to enter any values within that box should it be made available to them.

Share this post


Link to post
Share on other sites

The unix timestamp will not go back or forward when the clocks change. Did you look at the time function?

Share this post


Link to post
Share on other sites

I see ::)

 

I didnt realise the time function was unix time stamp.

 

Thanks for the quick answer.

 

The unix timestamp will not go back or forward when the clocks change. Did you look at the time function?

Share this post


Link to post
Share on other sites

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.