A Tutorial on how to integrate reCAPTCHA with CakePHP

in CakePHP/PHP/Tutorials & Samples

After releasing my tutorial for a complete login and authentication system using CakePHP, I have received a lot of positive comments and private emails. I have also received quite a few emails asking about how to integrate reCAPTCHA with the login system. So, here is a complete tutorial on how to integrate ReCAPTCHA with CakePHP. In this tutorial, I add reCAPTCHA to a user signup page and inform the user if the reCAPTCHA validation failed. The tutorial took me less than 30 minutes to setup and you can download it here. (Once again, I am not changing the default CakePHP theme. So below is what the signup screen looks like:)

recaptcha-screen

For those of you who are not aware, reCAPTCHA is a free CAPTCHA service provided by Google. A CAPTCHA is a program that can tell whether its user is a human or a computer. You’ve probably seen them — colorful images with distorted text at the bottom of Web registration forms. CAPTCHAs are used by many websites to prevent abuse from “bots,” or automated programs usually written to generate spam. No computer program can read distorted text as well as humans can, so bots cannot navigate sites protected by CAPTCHAs. If you are building a web app with a signup page or any page that accepts user content, then you should definitely integrate a CAPTCHA system on those pages.

recaptcha-types

As mentioned earlier, this tutorial starts from the code generated by the CakePHP login system tutrorial that I created earlier. So, that means that this tutorial is based on CakePHP 2.3…

Obtain your reCAPTCHA keys

The first thing that you need to do is signup at http://www.google.com/recaptcha/ to obtain your FREE reCAPTCHA keys for your site. They will provide you with public key and private key for your website.

Download the reCAPTCHA PHP library

You will need to download the PHP reCAPTCHA library since we will be using the library from our vendors folder in CakePHP. You can download the reCAPTCHA PHP library here. Now that we have our reCAPTCHA keys and the reCAPTCHA PHP library, we are ready to modify CakePHP.

Modify config.php

The first thing to do is to modify CakePHP’s config.php file to include the reCAPTCHA keys.

 /**
 * Recaptcha settings. Please replace these values by your actual recaptcha keys
 */	
Configure::write('reCAPTCHA.publicKey', 'Enter-Your-Public-Key-Here');	
Configure::write('reCAPTCHA.privateKey', 'Enter-Your-Private-Key-Here');	

Please make sure to update these keys with your actual reCAPTCHA keys.

Place the reCAPTCHA PHP library in the vendors folder

One of the really cool things about CakePHP is that it allows you to use third-party libraries by simply placing them inside the vendors folder. In the case of reCAPTCHA, we will simply place the file recaptchalib.php into the vendors folder.

Modify the UsersController.php file to support reCAPTCHA

The first thing to do is to ensure that the users controller can access the reCAPTCHA PHP library that was placed inside the vendors folder. You can do that by adding the following code:

App::import('Vendor', 'recaptcha', array('file' => 'recaptcha'. DS . 'recaptchalib.php'));  

We will then modify the add() function so that it takes reCAPTCHA into account. Here are some important notes about the function:

  • When we post data to the add() function, we first check to see if the reCAPTCHA passed. If it passes, then we let CakePHP do its thing and create the user. If it fails we stop the creation but call the validate funciton to see if there are additional validation errors…
  • This implementation goes against the principles of MVC because we should actually be doing the reCAPTCHA validation in the User model. But for the sake of simplicity, I am doing the reCAPTCHA validation in the controller. (Yes, I am cheating… If you want to follow proper convention, you can modify this code quite easily)
  • I have a flag called $recaptchaPassed that informs me whether the reCAPTCHA passed or failed. This parameter is passed to the add view

The full code for the add() function is displayed below:

    public function add() {
	$recaptchaPassed = true;
        if ($this->request->is('post')) {
		
		// verify recaptcha
		$recaptchaResp = recaptcha_check_answer (Configure::read("reCAPTCHA.privateKey"),
                  $_SERVER["REMOTE_ADDR"],
                    $this->request->data["recaptcha_challenge_field"],
                   $this->request->data["recaptcha_response_field"]);
	
			if (!$recaptchaResp->is_valid) {
				// server confirmed failure of recaptcha
				$recaptchaPassed = false;
				
				// we still want to validate other failures
				$this->User->set($this->request->data);
				$this->User->validates();
				
				// inform the user that validation failed
				$this->Session->setFlash(__('The user could not be created. Please, try again.'));
			}else{
				// all good, attempt to create our user
				$this->User->create();
				if ($this->User->save($this->request->data)) {
					$this->Session->setFlash(__('The user has been created'));
					$this->redirect(array('action' => 'index'));
				} else {
					$this->Session->setFlash(__('The user could not be created. Please, try again.'));
				}
			}			
        }
	$this->set(compact('recaptchaPassed'));
    }

Modify the user view file add.ctp

When someone adds themself to our system, we want to verify that the user is human by using reCAPTCHA. So a call to reCAPTCHA is made using AJAX to render the reCAPTCHA. To do so, we need to make a call to reCAPTCHA from add.ctp. I have created a div element called recaptcha_element that will be used to hold reCAPTCHA data sent from Google. I will update this element once the document is loaded by using CakePHP’s script block with the following code:

	// add the script to insert recaptcha
	$this->Html->scriptBlock("	
	$(function(){
		Recaptcha.create(\"".Configure::read("reCAPTCHA.publicKey")."\", \"recaptcha_element\", {
		theme: \"red\"});
	});
	",array('inline'=>false)); // inline => false will put this snippet in the DOM's head.

The code above creates the reCAPTCHA element using the public key that we stored in our config.php file. I also do a check for the variable recaptchaPassed so that I can display an error message in case the user failed reCAPTCHA validation. The following is the full code for add.ctp:

 
<!-- app/View/Users/add.ctp -->
<div class="users form">

<?php echo $this->Form->create('User');?>
    <fieldset>
        <legend><?php echo __('Add User'); ?></legend>
        <?php echo $this->Form->input('username');
		echo $this->Form->input('email');
        echo $this->Form->input('password');
		echo $this->Form->input('password_confirm', array('label' => 'Confirm Password *', 'maxLength' => 255, 'title' => 'Confirm password', 'type'=>'password'));
        echo $this->Form->input('role', array(
            'options' => array( 'king' => 'King', 'queen' => 'Queen', 'rook' => 'Rook', 'bishop' => 'Bishop', 'knight' => 'Knight', 'pawn' => 'Pawn')
        ));
?>
<div id="recaptcha_element"></div>
<?php 
	// add the script to insert recaptcha
	$this->Html->scriptBlock("	
	$(function(){
		Recaptcha.create(\"".Configure::read("reCAPTCHA.publicKey")."\", \"recaptcha_element\", {
		theme: \"red\"});
	});
	",array('inline'=>false)); // inline => false will put this snippet in the DOM's head.
?>
<?php if(!$recaptchaPassed){ ?>
<div class="error-message">Your image verification failed. Please try again.</div>
<?php } ?>
<?php	
	echo $this->Form->submit('Add User', array('class' => 'form-submit',  'title' => 'Click here to add the user') ); 
?>
    </fieldset>
<?php echo $this->Form->end(); ?>
</div>
<?php 
if($this->Session->check('Auth.User')){
	echo $this->Html->link( "Return to Dashboard",   array('action'=>'index') ); 
	echo "<br>";
	echo $this->Html->link( "Logout",   array('action'=>'logout') ); 
}else{
	echo $this->Html->link( "Return to Login Screen",   array('action'=>'login') ); 
}
?>

Modify your layout ctp file

As mentioned earlier, we are using AJAX to display our reCAPTCHA element. To do so, we need the file recaptcha_ajax.js which is hosted by Google on their CDN at https version as well so we are going to use https://www.google.com/recaptcha/api/js/recaptcha_ajax.js. This requires jQuery as well. So, i will be using https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js. For this tutorial, I am using the default CakePHP layout default.ctp. Here are the two lines that I added to the default.ctp layout file:

echo $this->Html->script('https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'); 
echo $this->Html->script('https://www.google.com/recaptcha/api/js/recaptcha_ajax.js'); 

That’s all folks!

That is all that is required to get CakePHP to play nice with reCAPTCHA. You can download the entire tutorial in RAR format here.

Tags:

Mifty Yusuf is a Montreal-based software developer who enjoys playing with new web technologies as well as comic books and illustrations. He beleives that, no matter what the question is, the answer is always Batman!

7 Comments

  1. i am a fresher…need guidance for cakephp searched allot but your blog helped me allot…..thnz allot for this fab blog
    i have a silly question where you have write the css of this page….i know nothing about cakephp hope you dont mind of answering this question…please…thanks in advance

  2. Great tutorial, thanks!
    Actually I found two small bugs:
    1) If you use App::import(‘Vendor’, ‘recaptcha’, array(‘file’ => ‘recaptcha’. DS . ‘recaptchalib.php’)); the library file has to go in the subfolder recaptcha actually instead of the vendor root directory.

    2) it is not the config.php in the /cake directory which has to integrate the public and private key variables, it is in fact the core.php in the /app directory. Otherwise the controller has no access to the variables and can not use them when calling recaptcha_check_answer()

Leave a Reply

Your email address will not be published.

*

Latest from CakePHP

Go to Top