User registration is a common thing in web development. To ensure that only human registered as a user, it is common to use Completely Automated Public Turing test to tell Computers and Humans Apart (CAPTCHA).

There are several CAPTCHA library available. This article will show how you can integrate 2 such library to provide CAPTCHA verification for user registration.

The library I used are kCAPTCHAand reCAPTCHA. Let’s start with reCAPTCHA.

reCAPTCHA is recommended as the official CAPTCHA implementation by the original CAPTCHA creators. Here is how you do it, step-by-step:

1. Download the library.

2. Sign up for the API key. reCAPTCHA is a web service so you need to sign up for API key to be able to use it. Notice on signing up , you will be asked to fill in a domain name. As far as I know you can put anything, I tried putting my domain and localhost, both accepted. After signing up you will have two keys one public key (for the user) and the other a private key (for communication between our server with reCAPTCHA server).

3. Extract and put the result at your vendors folder ( [web]/app/vendors/ ).

4. Create a component file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
class RecaptchaComponent extends Object
{
    var $publickey = "..."; // use your public key here
    var $privatekey = "..."; // user your private key here
 
    function startup(&$controller)
    {
        $this->controller = $controller;
    }
 
    function render()
    {
        vendor('recaptcha/recaptchalib');
 
        $error = null;
 
        echo recaptcha_get_html($this->publickey, $error);
    }
 
    function verify()
    {
        vendor('recaptcha/recaptchalib');
 
        $resp = recaptcha_check_answer ($this->privatekey,
                                  $_SERVER["REMOTE_ADDR"],
                                  $_POST["recaptcha_challenge_field"],
                                  $_POST["recaptcha_response_field"]);
 
        if ($resp->is_valid) {
            return true;
        } else {
            return false;
        }
    }
}
?>

store this at [web]/app/controllers/components/recaptcha.php

5. Create User Model and User Controller

Model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php
class User extends AppModel
{
    var $name = 'User';
 
    /**
     * validation rules
     */
    var $validate = array (
		'user_login' => array(	
			'exists' => array(
					'rule' => array( 'checkUnique', 'user_login' ),  
					'message' => 'The Username you entered has been taken.'
			),
			'minLength' => array(
					'rule' => array('minLength', 3),  
					'message' => 'Username must at least be 3 character long.'
			)
		),
		'user_pass' => array(
			'mingLength' => array(
				'rule' => array('minLength', '6'),
				'message' => 'Mimimum 6 characters long'
			)
		),
		'user_name' => array(
			'minLength' => array(
					'rule' => array('minLength', 3),  
					'message' => 'Username must at least be 3 character long.'
			)
		),
		'user_email' => array (
			'email' => array( 
				'rule' => 'email',
				'message' => 'Please supply a valid email address.'
			),
			'exists' => array(
				 'rule' => array( 'checkUnique', 'user_email' ),  
				 'message' => 'The email you entered has been registered.'
			)
		)
	);
 
	/** 
	* Validate if the data is unique.
	*
	* @param $data         The data to be compared.
	* @param $fieldName    The field name to check.
	* @return true         If the field name unique. False otherwise.
	*/
	function checkUnique( $data, $fieldName ) {
	        $valid = false;
		if(isset($fieldName) && $this->hasField($fieldName)) {
			$valid = $this->isUnique(array($fieldName => $data));
		}
		return $valid;
	}
}
?>

store this as [web]/app/models/users.php.

Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
class UsersController extends AppController
{
    var $name = 'Users';
    var $components = array('Auth', 'Recaptcha'); 
 
    function beforeFilter() {
        $this->Auth->fields = array('username' => 'user_login', 'password' => 'user_pass');
        $this->Auth->allow('register', 'recaptcha');
    }
 
 
    /**
     * This method handle the user registration process.
     * It will first of all, get the user basic information.
     * After user submit the information, a hash key will be generated and 
     * stored in the database. An email will then send to user and pending
     * for user activation
     */
    function register()
    {
        if ( !empty( $this->data ) ){
           if ( $this->data['User']['user_pass'] 
                    == $this->Auth->password( $this->data['User']['user_pass_confirm'])) {
                    if (  $this->Recaptcha->verify() ){
                        $temp = array( 
                                'user_registered'=> date('Y-m-d'), 
                                'user_activation_key'=>sha1( time() ), 
                                'user_status' => 0 );
                        $this->data['User'] = array_merge( $this->data['User'], $temp );
                        unset( $this->data['User']['captcha']);
 
                        $this->User->create();
                        if ( $this->User->save($this->data) ){
                            // do other stuff here like send activation email to user
                            // or/and redirect to other page
                        }
                    } else {
                        $this->Session->setFlash('captcha verification failed');
                    }
            } else {
                $this->Session->setFlash('password mismatch');
            }
        } 
    }
 
    function recaptcha()
    {
        $this->Recaptcha->render();
    }
}
?>

store this as [web]/app/controllers/users_controller.php.

6. Create the view

1
2
3
4
5
6
7
8
9
10
11
<?php
echo $form->create( 'User', array('action' => 'register' ) );
echo $form->input('user_login', array( 'label' => 'User Name :' ) );
echo $form->input('user_name', array( 'label' => 'Full name :' ) );
echo $form->input('user_email', array( 'label' => 'Email :') );
echo $form->input('user_pass', array( 'label' => 'Password :', 'type' => 'password') );
echo $form->input('user_pass_confirm', array( 'label' => 'Retype Password :', 'type' => 'password' ) );
$this->requestAction('users/recaptcha');
echo $form->submit();
echo $form->end();
?>

store this as [web]/app/views/register.ctp.

7. voila!, We are done!!

Now, let’s move on to kCAPTCHA. It is pretty much the same. Unlike reCAPTCHA, kCAPTCHA is not a web service. So you need not sign up for anything.

1. Download the library.

2. Extract and put the result at your vendors folder ( [web]/app/vendors/ ).

3. Create component.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class KCaptchaComponent extends Object
{
    function startup(&$controller)
    {
        $this->controller = $controller;
    }
 
    function render()
    {
        vendor('kcaptcha/kcaptcha');
        $kcaptcha = new KCAPTCHA();
        $this->controller->Session->write('captcha', $kcaptcha->getKeyString());
    }
}
?>

store this as [web]/app/controllers/components/kcaptcha.php

4. The Users model is the same as above.

5. The User controller is pretty much the same, but I will rewrite the whole code anyway.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
class UsersController extends AppController
{
    var $name = 'Users';
    var $components = array('Auth', 'KCaptcha'); 
 
    function beforeFilter() {
        $this->Auth->fields = array('username' => 'user_login', 'password' => 'user_pass');
        $this->Auth->allow('register','kcaptcha' );
    }
 
 
    /**
     * This method handle the user registration process.
     * It will first of all, get the user basic information.
     * After user submit the information, a hash key will be generated and 
     * stored in the database. An email will then send to user and pending
     * for user activation
     */
    function register()
    {
        if ( !empty( $this->data ) ){
           if ( $this->data['User']['user_pass'] 
                    == $this->Auth->password( $this->data['User']['user_pass_confirm'])) {
                   if ( strtolower($this->data['User']['captcha']) == strtolower( $this->Session->read('captcha')) ) {
                        $temp = array( 
                                'user_registered'=> date('Y-m-d'), 
                                'user_activation_key'=>sha1( time() ), 
                                'user_status' => 0 );
                        $this->data['User'] = array_merge( $this->data['User'], $temp );
                        unset( $this->data['User']['captcha']);
 
                        $this->User->create();
                        if ( $this->User->save($this->data) ){
                             // do other stuff here like send activation email to user
                            // or/and redirect to other page
                        }
                    } else {
                        $this->Session->setFlash('captcha verification failed');
                    }
            } else {
                $this->Session->setFlash('password mismatch');
            }
        } 
    }
 
    function kcaptcha()
    {
        $this->KCaptcha->render(); 
    }     
}
?>

store this as [web]/app/controllers/users_controller.php

6. The view

1
2
3
4
5
6
7
8
9
10
11
12
<?php
echo $form->create( 'User', array('action' => 'register' ) );
echo $form->input('user_login', array( 'label' => 'User Name :' ) );
echo $form->input('user_name', array( 'label' => 'Full name :' ) );
echo $form->input('user_email', array( 'label' => 'Email :') );
echo $form->input('user_pass', array( 'label' => 'Password :', 'type' => 'password') );
echo $form->input('user_pass_confirm', array( 'label' => 'Retype Password :', 'type' => 'password' ) );
echo $form->input('captcha', array( 'label' => 'Please type the text presented in the image : ',
                                    'before' => '<img src="'. $html->url('/users/kcaptcha') . '"') ); 
echo $form->submit();
echo $form->end();
?>

store this as [web]/app/views/register.ctp.

7. voila!, We are done!!

Here is same screenshots showing CAPTCHA user registration in action.

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Google
  • StumbleUpon

7 Responses to “CakePHP, Captcha and User Registration”

  1. Budi S Says:

    Interesting Article ..
    Keep the good posting Bro

  2. francky06l Says:

    Good article.
    The kCaptcha is, I think, vulnerable. I could register once, and then submitting many time the form (ie: not requesting it first), changing informations except the captcha string of my first registration.
    Solution: be sure to delete the string from Session in success/failure case.

    thanks.

  3. ankara evden eve nakliyat Says:

    very nice article.

  4. Saro Says:

    Hey, small fix for everyone experiencing trouble with CakePHP 1.2 RC2. Seems like the vendor() function has been depreciated.

    You must change:
    vendor(’recaptcha/recaptchalib’);

    on lines 14 and 26 to:

    App::import(’vendor’, ‘Recaptcha’, array(’file’=>’recaptcha/recaptchalib.php’));

    I hope this helps someone. :)

  5. protospike Says:

    This is an awesome article about CAPTCHA and email verification, thank you very much for sharing! Not only do I need the captcha I was also confused about the user activation e-mail which you have also covered here!

  6. yodi aditya Says:

    Keep rocks!

  7. William Notowidagdo Says:

    just what I need. Thank you!

Leave a Reply