How to make a PHP Guestbook with Math Captcha Security

How to make a PHP Guestbook with Math Captcha Security

13 2091

PHP Guestbook with using math captcha

Today I will tell you how to create your own guestbook with spam protection system – math captcha. This will and easy for your members and also good new protection from bots. In our guestbook we will use mySQL to store records.

Here are samples and downloadable package:

Live Demo
download in package

Ok, download the source files and lets start coding !


Step 1. SQL

Firstly – we should prepare SQL table to store records of our guestbook. Execute next SQL:

CREATE TABLE `s_guestbook` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(16) NOT NULL default '',
`name` varchar(32) NOT NULL default '',
`message` text  NOT NULL,
`when` INT(11) NOT NULL,
PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

We will be storing IP of sender, his name, message itself, and time of adding post (timestamp)

Step 2. CSS

Here are used CSS styles. Just few styles for our demo page:

css/main.css

body{background:#eee;font-family:Verdana, Helvetica, Arial, sans-serif;margin:0;padding:0}
.example{background:#FFF;width:625px;font-size:80%;border:1px #000 solid;margin:3.5em auto 2em;padding:1em 2em 2em}
.post {border:1px #DDD dashed;margin:5px;padding:5px;font-size:11px;width:95%}
.example form div{margin-bottom:5px;}

Step 3. PHP

Ok, here are all used PHP file:

index.php

<?php

// set error reporting level
if (version_compare(phpversion(), '5.3.0', '>=') == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);


require_once('inc/guestbook.inc.php');
$oGuestbook = new Guestbook();

$sRes = '<h2>You can add your post here</h2>';
if ($_POST['post'] != '' && $_POST['name'] != '' && $_POST['captcha'] != '') { // if something posted
    $iCaptcha = (int)$_POST['captcha'];
    $iCaptchaHash = md5($iCaptcha);
    session_start();
    $iOldCaptchaHash = $_SESSION['captcha_res'];

    if ($iCaptchaHash == $iOldCaptchaHash) { // perform posting
        $oGuestbook->addPost($_POST['name'], $_POST['post']);
        $sRes = '<h2>Your post successfully posted</h2>';
    } else {
        $sRes = '<h2>Captcha incorrect</h2>';
    }
}

list($sQuestion, $iRes) = $oGuestbook->getMathCaptcha();
$sPosts = $oGuestbook->getAllPosts();

// our page template
$sPageCode = <<<EOF
<link rel="stylesheet" href="css/main.css" type="text/css" />
<div class="example">
    <h3><a href="#">PHP Guestbook with using math captcha</a></h3>
    <div>
        <div style="margin-bottom:10px;">
            <h4>{result}</h4>
            <form method="post" action="index.php">
                <div>Your name:</div>
                <div><input type="text" name="name" value="" /></div>
                <div>Your guestbook post:</div>
                <div><textarea name="post"></textarea></div>
                <div>Captcha:</div>
                <div>{captcha}</div>
                <div>Verification (Type what you see):</div>
                <div><input type="text" name="captcha" value="" /></div>
                <div><input type="submit" name="submit" value="Submit" /></div>
            </form>
        </div>
        <div>
            <h4>Other Guestbook posts</h4>
            {guestbook_posts}
        </div>
    </div>
</div>
EOF;

$aReplaces = array(
    '{captcha}' => $sQuestion,
    '{result}' => $sRes,
    '{guestbook_posts}' => $sPosts,
);
echo strtr($sPageCode, $aReplaces);

?>

This file draw us posting form with math captcha, also it checking typed posted captcha value, and if all fine – then allow to add post. Also, as you noticed, I started using ‘strtr’ function in generation of result HTML code. Very easy to replace keys to values in template file via array of replaces.

inc/guestbook.inc.php

<?php

// guestbook class
class Guestbook {

    // DB variables
    var $sDbName;
    var $sDbUser;
    var $sDbPass;

    // constructor
    function Guestbook() {
        $this->sDbName = 'DB_NAME';
        $this->sDbUser = 'DB_USER';
        $this->sDbPass = 'DB_PASS';
    }

    // adding to DB table posted record
    function addPost($sNameUnsafe = '', $sPostUnsafe = '') {
        if ($sPostUnsafe != '') {
            $vLink = mysql_connect('localhost', $this->sDbUser, $this->sDbPass); // the host, name, and password for your mysql
            mysql_select_db($this->sDbName); // select the database
            $sName = mysql_real_escape_string(strip_tags($sNameUnsafe));
            $sMessage = mysql_real_escape_string(strip_tags($sPostUnsafe));
            $iVisitorIp = $this->getVisitorIp();
            if ($sMessage != '' && $iVisitorIp)
                mysql_query("INSERT INTO `s_guestbook` SET `ip`='{$iVisitorIp}', `name`='{$sName}', `message`='{$sMessage}', `when`=UNIX_TIMESTAMP()");

            mysql_close($vLink);
        }
    }

    function getAllPosts() {
        $vLink = mysql_connect('localhost', $this->sDbUser, $this->sDbPass);
        mysql_select_db($this->sDbName); // select the database
        $vRes = mysql_query('SELECT * FROM `s_guestbook` ORDER BY `id` DESC LIMIT 15'); // returning the last 15 posts

        $sMessages = '';
        if ($vRes) {
            while($aMessages = mysql_fetch_array($vRes)) {
                $sWhen = date('H:i:s', $aMessages['when']);
                $sMessages .= '<div class="post">' . $aMessages['name'] . ': ' . $aMessages['message'] . '<span> (' . $sWhen . ')</span></div>';
            }
        } else {
            $sMessages = 'DB error, create SQL table before';
        }
        mysql_close($vLink);
        return $sMessages;
    }

    function getMathCaptcha() {
        $aOps = array('+', '-', '*'); // possible operators
        $iVal1 = rand(1,10); // first variable
        $iVal2 = rand(1,10); // second variable

        $i = array_rand($aOps, 1); // random operator index
        $sRandOp = $aOps[$i]; // random operator
        $sQuestion = "{$iVal1} {$sRandOp} {$iVal2}"; // generation of question
        $sQuestionEval = "\$iRes = {$iVal1} {$sRandOp} {$iVal2};";
        eval($sQuestionEval);

        session_start();
        $sHashRes = md5($iRes);
        $_SESSION['captcha_res'] = $sHashRes; // store md5 result in session param

        return array($sQuestion, $iRes);
    }

    function getVisitorIp() {
        $ip = '0.0.0.0';
        if ((isset($_SERVER['HTTP_X_FORWARDED_FOR'])) && (! empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } elseif ((isset($_SERVER['HTTP_CLIENT_IP'])) && (! empty($_SERVER['HTTP_CLIENT_IP']))) {
            $ip = explode('.',$_SERVER['HTTP_CLIENT_IP']);
            $ip = $ip[3].'.'.$ip[2].'.'.$ip[1].'.'.$ip[0];
        } elseif ((! isset($_SERVER['HTTP_X_FORWARDED_FOR'])) || (empty($_SERVER['HTTP_X_FORWARDED_FOR']))) {
            if ((! isset($_SERVER['HTTP_CLIENT_IP'])) && (empty($_SERVER['HTTP_CLIENT_IP']))) {
                $ip = $_SERVER['REMOTE_ADDR'];
            }
        }
        return $ip;
    }
}

?>

This is library class for our Guestbook. It contain several necessary functions: addPost – for adding new posts, getAllPosts – return us last 15 records from database, getMathCaptcha – get math capthca, getVisitorIp – get visitor IP (which we will storing for records)


Live Demo
download in package

Conclusion

I hope that made interesting sample today, its contain two interesting ideas – creating Guestbook itself plus Math captcha to protect from bots. Good luck!


13 COMMENTS

  1. I have implemented this guestbook in the following URL: http://www.simtanda.com/webdesign_seo_ppc_etc/gaestebog.php

    And this is what i get (Why, because it works perfect in the following URL: http://www.simtanda.com/webdesign_seo_ppc_etc/gaestebog/index.php):

    Warning: session_start(): Cannot send session cache limiter – headers already sent (output started at /customers/simtanda.com/simtanda.com/httpd.www/webdesign_seo_ppc_etc/gaestebog.php:114) in /customers/simtanda.com/simtanda.com/httpd.www/webdesign_seo_ppc_etc/gaestebog/inc/guestbook.inc.php on line 87

  2. How can you have the input in the fields, i.e. name, comment be retained if the captcha is entered in wrong. Presently the page refreshes and erases all the information entered.
    Tried entering Your name:<input type="text" name="name" value="”/> however, it then shows in the field
    Thank you

    • Yes, of course this is possible too and easy. Example, lets change

      to

      as example, after (or even before) if ($_POST['po ...
      lets add next code:
      $sPostName = ($_POST['name'] != ”) ? $_POST['name'] : ”;
      so, if something was posted with its name – we will pickup its value.

  3. Thanks for the quick reply, however I must be missing something. Tried placing $sPostName = ($_POST['name'] != ”) ? $_POST['name'] : ”; in numerous places, i.e. $sPostName = ($_POST['name'] != ”) ? $_POST['name'] : ”;
    $sRes = ‘You can add your post here’;
    if ($_POST['post'] != ” && $_POST['name'] != ” && $_POST['captcha'] != ” )
    and it do not retain the value entered in name section when captcha was entered incorrectly. If you have the time, can you please be a bit more specific on code and placement. Thank you

  4. Yes, all fine, but I forget to tell that you will need to change
    <input type="text" name="name" value="" />
    to
    <input type="text" name="name" value="{$sPostName}" />
    so our field will take value based on this prepared variable

    • Hi toni,
      You always can expand your DB table structure and add email field here. After, you have to update result index.php file (and draw that new field for email), and finally – you have to accept that new email field and add into database.
      Pagination possible of course, but usually this is some custom made realization.

  5. Hey,
    Thanks so much for the tutorial, it’s great! I was just wondering how would you go about creating different pages, one to display the guestbook posts in the last week and one for the posts in the last day?
    Cheers!

    • Hello Matt,
      Actually, this is easy, all you need – make different selections (of time ranges). You can expand it in function getAllPosts of inc/guestbook.inc.php

  6. Hi

    i keep trying this script on my own website and keeps coming up with Captcha incorrect?

    Any ideas

    Kind regards

    Ian

    • Hello Ian,
      I am not sure how it is possible. Make sure that you used correct captcha answers, and you also can check, if your website already contains ‘session_start’ or not (in this case, you don’t need to call it in the second time)

Leave a Reply