15

Protect a form with a captcha

Create site10 by copying site9.

  1. /cms
    1. ...
    2. site9
    3. site10

In this chapter, we are going to program a means to protect the site from robots which try to user a form.

Read the article Send an HTTP request to a web service to learn to write a program which exploits another application on the web.

To test the result online, enter http://www.frasq.org/cms/site10 in the address bar of your navigator. Fill in the form while copying the captcha in the input field to the right of the image. Send the form. Try sending the form without copying the captcha.

Start by creating an action called captcha by adding the file captcha.php in the folder actions with the following content:

  1. /cms/site10
    1. actions
      1. captcha.php
  1. require_once 'strrand.php';
  2. require_once 'strtag.php';
  3.  
  4. function captcha($lang) {
  5.     $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  6.  
  7.     $code = strrand($charset, 4);
  8.  
  9.     $_SESSION['captcha'] = $code;
  10.    
  11.     $img = strtag($code);
  12.  
  13.     header('Content-Type: image/png');
  14.     header("Content-Disposition: inline; filename=captcha.png");
  15.  
  16.     imagepng($img);
  17.     imagedestroy($img);
  18.  
  19.     return false;
  20. }

The captcha action generates a random code of 4 capital letters, memorizes it in the session and returns a document containing an image which displays the code.

To give access to the captcha action, add an alias in the file includes/aliases.inc:

  1. $aliases = array(
  2.     array(
  3.         'captcha'               => 'captcha',
  4.     ),

Notice that the alias doesn't depend on the language.

Add the files strrand.php and strtag.php in the folder library with the following contents:

  1. function strrand($charset, $len) {
  2.     $max=strlen($charset)-1;
  3.     $s = '';
  4.  
  5.     srand();
  6.  
  7.     for ($i=0; $i < $len; $i++) {
  8.         $s .= $charset[rand(0, $max)];
  9.     }
  10.  
  11.     return $s;
  12. }

strrand returns a random sequence of characters taken from $charset of length $len.

  1. function strtag($text) {
  2.     $len=strlen($text);
  3.  
  4.     $fontfile=ROOT_DIR  . DIRECTORY_SEPARATOR . 'font.ttf';
  5.     $fontsize=24.0;
  6.  
  7.     $bbox = imageftbbox($fontsize, 0, $fontfile, $text);
  8.  
  9.     $w=$bbox[2]+($len-1)*20;
  10.     $h=40;
  11.  
  12.     $img = @imagecreatetruecolor($w, $h) or die();
  13.  
  14.     $bg=imagecolorallocate($img, 255, 255, 224);
  15.     $fg=imagecolorallocate($img, 64, 64, 64);
  16.  
  17.     imagefill($img, 0, 0, $bg);
  18.  
  19.     // print text unevenly
  20.     for ($x=15, $i=0; $i<$len; $i++) {
  21.         $y = rand($h/2,$h/2+15);
  22.         $r = rand(-45, 45);
  23.         imagettftext($img, $fontsize, $r, $x, $y, $fg, $fontfile, $text[$i]);
  24.         $x += rand(25, 35);
  25.     }
  26.  
  27.     // blur with colored dots
  28.     for ($i=0; $i<$w*$h/2.0; $i++) {
  29.         $color=imagecolorallocate($img, rand(128,255), rand(128,255), rand(128,255));
  30.         imagesetpixel($img, rand(0,$w-1), rand(0,$h-1), $color);
  31.     }
  32.  
  33.     return $img;
  34. }

strtag returns an image spotted with random color dots containing the characters of $text sufficiently misplaced and rotated to make the text impossible to interpret by a program but still readable by a human.

Install the file of a TTF character font directly in the root directory of the site:

  1. /cms/site10
    1. font.ttf

Enter http://localhost/cms/site10/captcha in the address bar of your navigator to display the captcha. Press F5 to display another one.

To add a captcha to the contact page, modify the file mailme.phtml in the folder views/en:

  1. <p class="input">
  2. <img class="captcha" src="<?php echo $base_path; ?>/captcha/mailme" alt="" title="Verification code" />
  3. &nbsp;:&nbsp;
  4. <input type="text" name="mailme_code" id="mailme_code" size="4" maxlength="4" title="4 letters" onkeypress="return submitonenter(event, 'mailme_send')" value="" />
  5. </p>

Adds the captcha and the input field mailme_code.

  1. <?php if ($missing_code): ?>
  2. <p>Enter the verification code displayed in the image.</p>
  3. <?php elseif ($bad_code): ?>
  4. <p>The verification code is incorrect.</p>
  5. <?php endif; ?>

Displays en error message if the code is missing or if it doesn't match the content of the image.

Modify the French version too:

  1. <p class="input">
  2. <img class="captcha" src="<?php echo $base_path; ?>/captcha/mailme" alt="" title="Code de vérification" />
  3. &nbsp;:&nbsp;
  4. <input type="text" name="mailme_code" id="mailme_code" size="4" maxlength="4" title="4 lettres" onkeypress="return submitonenter(event, 'mailme_send')" value="" />
  5. </p>

Notice that pressing return in the input field for the code sends the form.

  1. <?php if ($missing_code): ?>
  2. <p>Entrez le code de vérification affiché dans l'image.</p>
  3. <?php elseif ($bad_code): ?>
  4. <p>Le code de vérification est incorrect.</p>
  5. <?php endif; ?>

Modify the mailme function in the file blocks/mailme.php to add the analysis of the captcha:

  1.     $mail=$subject=$message=$code=false;

Initializes the variable which will hold the code returned by the form.

  1.             if (isset($_POST['mailme_code'])) {
  2.                 $code=readarg($_POST['mailme_code'], true);
  3.             }

Reads the code returned by the form.

  1.     $missing_code=false;
  2.     $bad_code=false;

Initializes the error flags.

  1.         case 'send':
  2.             if (!$code) {
  3.                 $missing_code=true;
  4.                 break;
  5.             }
  6.             $captcha=isset($_SESSION['captcha']) ? $_SESSION['captcha'] : false;
  7.             if (!$captcha or $captcha != strtoupper($code)) {
  8.                 $bad_code=true;
  9.                 break;
  10.             }

Checks if the code has been transmitted and if it matches the value memorized in the session.

  1.         case 'send':
  2.             if ($missing_code or $bad_code or $bad_mail or $missing_subject or $bad_subject or $missing_message) {
  3.                 break;
  4.             }

Skips any further treatment if the code is missing or wrong. IMPORTANT: The program must leave as soon as possible if the captcha isn't valid.

  1.     $errors = compact('missing_code', 'bad_code', 'missing_mail', 'bad_mail', 'missing_subject', 'bad_subject', 'missing_message', 'internal_error');

Adds the error flags $missing_code and $bad_code to the context of the view.

Modify the function bootstrap in the file library/bootstrap.php to add the opening of a session:

  1. require_once 'session.php';
  2.  
  3. function bootstrap() {
  4.     global $base_url, $base_path, $base_root;
  5.     global $session_name;

Loads the code which opens the session. Declares the global variable which holds the name of the session.

  1.     if (!isset($session_name)) {
  2.         list( , $session_name) = explode('://', $base_url, 2);
  3.         $session_name = 'FQ' . $session_name;
  4.  
  5.         if (ini_get('session.cookie_secure')) {
  6.             $session_name .= 'SSL';
  7.         }
  8.     }
  9.  
  10.     session_name(md5($session_name));
  11.     session_open();

Builds a unique session name. Defines the name of the session for PHP. Note that if the name of the session is already defined, typically in the configuration file, it is preserved.

Add the file session.php in the folder library with the following content:

  1. function session_reopen() {
  2.     session_close();
  3.     session_open();
  4. }
  5.  
  6. function session_open() {
  7.     session_start();
  8. }
  9.  
  10. function session_close() {
  11.     session_destroy();
  12.     $_SESSION=array();
  13. }

These 3 functions are simple wrappers which open and close the session. IMPORTANT: The name of the session has been defined by the bootstrap function.

Enter http://localhost/cms/site10/en/contact in the address bar of your navigator. Check that the form works if the code in the captcha is valid, missing or wrong.

Comments

To add a comment, click here.