0

I found a CAPTCHA script online for a self-made contact form on my site.

In my theme folder I have a file called captcha_code_file.php:

<?php 
/*
*
* this code is based on captcha code by Simon Jarvis 
* http://www.white-hat-web-design.co.uk/articles/php-captcha.php
*
* This program is free software; you can redistribute it and/or 
* modify it under the terms of the GNU General Public License 
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
* GNU General Public License for more details: 
* http://www.gnu.org/licenses/gpl.html
*/

session_start();

//Settings: You can customize the captcha here
$image_width = 110;
$image_height = 35;
$characters_on_image = 6;
$font = './monofont.ttf';

//The characters that can be used in the CAPTCHA code.
//avoid confusing characters (l 1 and i for example)
$possible_letters = '23456789bcdfghjkmnpqrstvwxyz';
$random_dots = 0;
$random_lines = 20;
$captcha_text_color="0x#B9B098";
$captcha_noice_color = "0x#CEC7BA";

$code = '';


$i = 0;
while ($i < $characters_on_image) { 
$code .= substr($possible_letters, mt_rand(0, strlen($possible_letters)-1), 1);
$i++;
}


$font_size = $image_height * 0.75;
$image = @imagecreate($image_width, $image_height);


/* setting the background, text and noise colours here */
$background_color = imagecolorallocate($image, 255, 255, 255);

$arr_text_color = hexrgb($captcha_text_color);
$text_color = imagecolorallocate($image, $arr_text_color['red'], 
        $arr_text_color['green'], $arr_text_color['blue']);

$arr_noice_color = hexrgb($captcha_noice_color);
$image_noise_color = imagecolorallocate($image, $arr_noice_color['red'], 
        $arr_noice_color['green'], $arr_noice_color['blue']);


/* generating the dots randomly in background */
for( $i=0; $i<$random_dots; $i++ ) {
imagefilledellipse($image, mt_rand(0,$image_width),
 mt_rand(0,$image_height), 2, 3, $image_noise_color);
}


/* generating lines randomly in background of image */
for( $i=0; $i<$random_lines; $i++ ) {
imageline($image, mt_rand(0,$image_width), mt_rand(0,$image_height),
 mt_rand(0,$image_width), mt_rand(0,$image_height), $image_noise_color);
}


/* create a text box and add 6 letters code in it */
$textbox = imagettfbbox($font_size, 0, $font, $code); 
$x = ($image_width - $textbox[4])/2;
$y = ($image_height - $textbox[5])/2;
imagettftext($image, $font_size, 0, $x, $y, $text_color, $font , $code);


/* Show captcha image in the page html page */
header('Content-Type: image/jpeg');// defining the image type to be shown in browser widow
imagejpeg($image);//showing the image
imagedestroy($image);//destroying the image instance
$_SESSION['6_letters_code'] = $code;

function hexrgb ($hexstr)
{
  $int = hexdec($hexstr);

  return array("red" => 0xFF & ($int >> 0x10),
               "green" => 0xFF & ($int >> 0x8),
               "blue" => 0xFF & $int);
}
?>

I can't seem to store the session created. What do I need to do to make this work? And how do I get the session in another file, e.g. if(empty($_SESSION['6_letters_code'])?

1
  • This question has absolutely nothing to do with WordPress.
    – Chris_O
    Apr 10, 2014 at 23:45

2 Answers 2

4
+25

The definitive solution with session is.. do not use session.

As example in your form, you can output 3 fields:

  • an image field -> the captcha image
  • a text field -> to allow users to insert the characters
  • an hidden field -> contain the solution but encripted using wp_create_nonce

In this way when the form is submitted you can compare the user input with the hidden field input using wp_verify_nonce and you solve your problem without using session.

I'll write the code in a class, you can put all this code in a file, name it 'mycaptcha.php' and save it in the theme folder, after that, require_once it from functions.php.

<?php
class MyCaptcha {
  protected $code = '';
  protected $letters = '23456789bcdfghjkmnpqrstvwxyz';
  protected $chars_num;
  protected $tcolor;
  protected $ncolor;
  protected $width;
  protected $height;

  /**
   * Allow to configure some options, generate a random code and print the fields
   */
  function __construct( $args = array() ) {
    $defaults = array(
      'chars_num' => 6,
      'dots' => 0,
      'lines' => 20,
      'width' => 110,
      'height' => 35,
      'tcolor' => 'B9B098',
      'ncolor' => 'CEC7BA'
    );
    $args = wp_parse_args( $args, $defaults );
    foreach ( $args as $k => $v ) {
      $this->$k = $v;
    }
    $this->setCode();
    $this->fields();
  }

  /**
   * Print the fields
   */
  protected function fields() {
    echo $this->getHidden();
    echo $this->getImg();
    echo $this->getText();
  }

  /**
   * Generate a random code
   */    
  protected function setCode() {
    $i = 0;
    $len = strlen( $this->letters ) - 1;
    while ( $i < $this->chars_num ) { 
      $this->code .= substr( $this->letters, mt_rand( 0, $len ), 1 );
      $i++;
    }
  }

  /**
   * Print hidden field
   */ 
  protected function getHidden() {
    return wp_nonce_field( $this->code , md5(__CLASS__) . '_n', FALSE, FALSE );
  }

  /**
   * Print text field
   */     
  protected function getText() {
    $f = '<label>%s<input type="text" name="%s" value="" autocomplete="off" /></label>';
    return sprintf( $f, __('Type the captcha:', 'your-textdomain'),  md5(__CLASS__) );
  }

  /**
   * Print captcha image
   */      
  protected function getImg() {
    $f = '<img src="%s" width="%d" height="%d" alt="" />';
    $data = array(
      'c' => self::encode( $this->code ),
      'size' => "{$this->width}x{$this->height}",
      'dots' => "{$this->dots}",
      'lines' => "{$this->lines}",
      'tcolor' => "{$this->tcolor}",
      'ncolor' => "{$this->ncolor}"
    );
    $url = add_query_arg( $data, get_template_directory_uri() . '/captcha_img.php' );
    return sprintf( $f, $url, $this->width, $this->height );
  }

  /**
   * Verify the nonce
   */      
  static function verify() {
    $method = filter_input( INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_STRING );
    $type = strtoupper( $method ) === 'GET' ? INPUT_GET : INPUT_POST;
    $nonce = filter_input( $type, md5(__CLASS__) . '_n', FILTER_SANITIZE_STRING );
    $code = filter_input( $type, md5(__CLASS__), FILTER_SANITIZE_STRING );
    return ! empty($code) &&  ! empty($nonce) && wp_create_nonce( $code ) === $nonce;
  }

  /**
   * Convert an hexadecimal color to an rgb array color
   */      
  static function hexrgb( $hex ) {
    $int = hexdec( $hex );
    return array(
      "red" => 0xFF & ($int >> 0x10),
      "green" => 0xFF & ($int >> 0x8),
      "blue" => 0xFF & $int
    );
  }

  /**
   * encode the code to avoid put plan code in image url
   */
  private static function encode( $code ) {
    $iv_size = mcrypt_get_iv_size( MCRYPT_BLOWFISH , MCRYPT_MODE_ECB );
    $iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
    $key = md5( __CLASS__ );
    $enc = mcrypt_encrypt( MCRYPT_BLOWFISH , $key, $code, MCRYPT_MODE_ECB, $iv);
    return urlencode( base64_encode( $enc ) );
  }

  /**
   * Decode the code
   */
  static function decode( $encoded ) {
    $code = urldecode( base64_decode( $encoded ) );
    $iv_size = mcrypt_get_iv_size( MCRYPT_BLOWFISH , MCRYPT_MODE_ECB );
    $iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
    $key = md5( __CLASS__ );
    return mcrypt_decrypt( MCRYPT_BLOWFISH , $key, $code, MCRYPT_MODE_ECB, $iv );
  }

}

Please be sure there is no spaces or blank lines at the strat (before <?php) nor at the end of the file.

The class require another file in your theme folder captcha_img.php that should be put in your theme folder. This file will contain the code you posted, just a little modified:

<?php 
// require the class
@require_once __DIR__ . '/mycaptcha.php';

// defaults
$w = 110;
$h = 35; 
$random_dots = 0;
$random_lines = 20;
$captcha_text_color = "0x#B9B098";
$captcha_noice_color = "0x#CEC7BA";

/* get code */
$code = filter_input( INPUT_GET, 'c', FILTER_SANITIZE_STRING );
if ( empty( $code ) ) die( '' );
$code = MyCaptcha::decode( $code );

/* get settings */
$sizes = filter_input( INPUT_GET, 'size', FILTER_SANITIZE_STRING );
if ( ! empty( $sizes ) ) {
  $s = explode( 'x', $sizes );
  if ( is_numeric($s[0]) && (int) $s[0] > 0  ) $w = (int) $s[0];
  if ( isset($s[1]) && is_numeric($s[1]) && (int) $s[1] > 0  ) $h = (int) $s[1];
}
$dots = (int) filter_input( INPUT_GET, 'dots', FILTER_SANITIZE_NUMBER_INT );
if ( $dots > 0 ) $random_dots = $dots;
$lines = (int) filter_input( INPUT_GET, 'lines', FILTER_SANITIZE_NUMBER_INT );
if ( $dots > 0 ) $random_lines = $lines;
$tcolor = (string) filter_input( INPUT_GET, 'tcolor', FILTER_SANITIZE_STRING );
if ( strlen($tcolor) === 6 ) $captcha_text_color = "0x#{$tcolor}";
$ncolor = (string) filter_input( INPUT_GET, 'ncolor', FILTER_SANITIZE_STRING );
if ( strlen($ncolor) === 6 ) $captcha_noice_color = "0x#{$ncolor}";

/* prepare image */
$font = './monofont.ttf';
$font_size = $h * 0.75;
$image = @imagecreate($w, $h);
$bg_color = imagecolorallocate($image, 255, 255, 255);
$arr_text_color = MyCaptcha::hexrgb($captcha_text_color);
$text_color = imagecolorallocate(
  $image,
  $arr_text_color['red'], 
  $arr_text_color['green'],
  $arr_text_color['blue']
);
$arr_noice_color = MyCaptcha::hexrgb($captcha_noice_color);
  $image_noise_color = imagecolorallocate(
  $image,
  $arr_noice_color['red'], 
  $arr_noice_color['green'],
  $arr_noice_color['blue']
);

/* generating dots randomly in background of image */
for( $i=0; $i < $random_dots; $i++ ) {
  imagefilledellipse(
    $image,
    mt_rand(0, $w), mt_rand(0, $h),
    2, 3,
    $image_noise_color
  );
}

/* generating lines randomly in background of image */
for( $i=0; $i < $random_lines; $i++ ) {
  imageline(
    $image,
    mt_rand(0, $w), mt_rand(0, $h),
    mt_rand(0, $w), mt_rand(0, $h),
    $image_noise_color
  );
}

/* create a text box and add 6 letters code in it */
$textbox = imagettfbbox( $font_size, 0, $font, $code ); 
$x = ( $w - $textbox[4] ) / 2;
$y = ( $h - $textbox[5] ) / 2;
imagettftext( $image, $font_size, 0, $x, $y, $text_color, $font , $code);

/* output captcha image */
header('Content-Type: image/jpeg');
imagejpeg($image);
imagedestroy($image); 

Again, be sure there is no spaces or blank lines at the start (before <?php) nor at the end of the file.

How to use

Print fields

In the forms simple use <?php new MyCaptcha; ?> to output the 3 fields using default settings.

You can customize the number of letters, the width and the height of the image, the colors, the number of lines and dots passing an array of arguments to constructor, e.g.

 <div class="my_captcha">
   <?php
   $args = array(
     'chars_num'=> '8', // number of characters
     'tcolor' => 'FFFFFF', // text color
     'ncolor' => '999999', // noise color
     'dots' => 50, // number of dots
     'lines'=> 40 // number of lines,
     'width'=>'220' // number of width,
     'height'=>'70' // number of height
   );
   new MyCaptcha( $args );
   ?>
 </div>

Verify

In the file or in the function that process the form you can verify simply using:

$is_captcha_corrent = MyCaptcha::verify();

Nothing more. Of course, if you use an external file to process the form, this file have to bootstrap WordPress.

Both files are available in a Gist here.

0

This question is answered correctly by @gmazzap. Now in the year 2022, I too was having the same session's issue while validating the captcha for my custom form in WordPress. I used the above answer, was working fine until the mcrypt part. Mcrypt is deprecated and no longer used. So I replaced the encode and decode functions in mycaptcha.php file with the following:

//encode

private static function encode( $code ) {

$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$key = md5( __CLASS__ );
$encrypted = openssl_encrypt($code, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);

}

//decode

static function decode( $encoded )

{

list($encrypted_data, $iv) = explode('::', base64_decode($encoded), 2);
$key = md5( __CLASS__ );
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);

}

For Verification where you are processing your form, you can do:

if(!$is_captcha_corrent === true){

echo "<script>alert('invalid captcha');</script>";

}

Works fine for me (Note:* Please remove any white spaces and copy the full function code properly).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.