Integrating Google’s reCaptcha in CodeIgniter’s form validation – the callback way

Well, it’s that time when someone is bombarding you with spam or, who knows, is trying to look for vulnerabilities by filling your site’s form with all sorts of garbage strings. And then the whole Earth falls on you. What to do then?

Captcha image. Simple. There is already a great tutorial about integrating Google’s new reCaptcha system using PHP on 9lessons.info (by the way, that one is a great tutorial site. I sure hope Srinivas Tamada won’t mind if I’ll use a big part of his tutorial on this, including the images…).

So let’s integrate a captcha system into our CodeIgniter application too. How do we do that?

First, let’s get ourselves set up.

Get a (or two…) reCaptcha Key(s)

You first need to have the security keys that will help with the communication between your application and Google’s service.

So go here: https://www.google.com/recaptcha/intro/index.html

…and push the “Get reCAPTCHA” button (I hope you are already logged into Google).

Now you only have to register your website…

And you will receive a Google Site Key, which will be used for the form:


…and a Google Secret key which will be used for communicating with Google:

The form

Now, let’s start by creating the form. In here I will create a bare page, so please don’t jump over me because I’ve not set some “important” html tags. We create a new file named form_view.php inside the views directory:

<!doctype html>

<html lang="en">
<head>
<meta charset="utf-8">
<title>reCaptcha form</title>
<script src="https://www.google.com/recaptcha/api.js"></script>
</head>

<body>
  <?php
  echo '<h1>The form</h1>';
  echo form_open();
  echo form_label('Name:','name').'<br />';
  echo form_input('name',set_value('name')).'<br />';
  echo '<div class="g-recaptcha" data-sitekey="IN_HERE_YOU_PLACE_YOUR_GOOGLE_SITE_KEY"></div>';
  echo form_submit('submit','Add');
  echo form_close();
  ?>
</body>
</html>

As you can see, in the head of the page we’ve introduced the api.js script, which will retrieve the captcha zone.

Also, inside the form we’ve introduced the div which is a placeholder for the captcha zone, having “data-sitekey” equal to the Google Site Key, you’ve received.

After this let’s go to our controller and verify if the visitor respected our form validation rules. If he/she did we will act accordingly:

<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller
{

  function __construct()
  {
    parent::__construct();
  }

  public function index()
  {
    $this->load->helper('form');
    $this->load->library('form_validation');

    $this->form_validation->set_rules('name','Name','trim|required');

    if($this->form_validation->run()===FALSE)
    {
      $this->load->view('form_view');
    }
    else
    {
      echo 'passed';
    }
}

But what about the captcha field? Where is it? Don’t we have to validate that? Of course we have to. That’s the point of this tutorial. But how do we do it.

Validate captcha with a callback

The fastest way would be to create a callback. So let’s just do this. According to the CodeIgniter manual, to create a callback function is pretty simple. We prepend the validation rule with “callback_” string, and create a function (method) with that rule as name. So our controller would now look like this:

class Welcome extends CI_Controller
{
  function __construct()
  {
    parent::__construct();
  }

  public function index()
  {
    $this->load->helper('form');
    $this->load->library('form_validation');

    $this->form_validation->set_rules('name','Name','trim|required');
    $this->form_validation->set_rules('g-recaptcha-response','Captcha','callback_recaptcha');

    if($this->form_validation->run()===FALSE)
    {
      $this->load->view('form_view');
    }
    else
    {
      echo 'passed';
    }

  }

  public function recaptcha($str)
  {
    if(!isset($str))
    {
      $this->form_validation->set_message('recaptcha', 'The {field} field is telling me that you are a robot. Shall we give it another try?');
      return FALSE;
    }
    else
    {
      return TRUE;
    }
  }
}

As you can see we’ve set an error message named recaptcha, so let’s change the form_view.php too, so that it will echo the error in case there is one.

<!doctype html>

<html lang="en">
<head>
<meta charset="utf-8">
<title>reCaptcha form</title>
<script src="https://www.google.com/recaptcha/api.js"></script>
</head>

<body>
  <?php
  echo '<h1>The form</h1>';
  echo form_open();
  echo form_label('Name:','name').'<br />';
  echo form_error('name','<div style="color:red;">','</div>');
  echo form_input('name',set_value('name')).'<br />';
  echo form_error('g-recaptcha-response','<div style="color:red;">','</div>');
  echo '<div class="g-recaptcha" data-sitekey="IN_HERE_YOU_PLACE_YOUR_GOOGLE_SITE_KEY"></div>';
  echo form_submit('submit','Add');
  echo form_close();
  ?>
</body>
</html>

Ok… so if we fill nothing in the form we will get both errors. All is well… Now let’s just work on our recaptcha() method so that it will verify with Google if the visitor is a robot or not.

public function recaptcha($str='')
{
  $google_url="https://www.google.com/recaptcha/api/siteverify";
  $secret='IN_HERE_YOU_PLACE_YOUR_SECRET_KEY_NOT_THE_SITE_KEY';
  $ip=$_SERVER['REMOTE_ADDR'];
  $url=$google_url."?secret=".$secret."&response=".$str."&remoteip=".$ip;
  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, $url);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($curl, CURLOPT_TIMEOUT, 10);
  curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16");
  $res = curl_exec($curl);
  curl_close($curl);
  $res= json_decode($res, true);
  //reCaptcha success check
  if($res['success'])
  {
    return TRUE;
  }
  else
  {
    $this->form_validation->set_message('recaptcha', 'The reCAPTCHA field is telling me that you are a robot. Shall we give it another try?');
    return FALSE;
  }
}

OK… Let’s review our code. Why did I give an empty string as fallback for my method? Because I don’t really care what I get, as long as I pass it to Google service. Using curl we can retrieve the result of the CAPTCHA test. If there’s a $res[‘success’] in the response, then that means that the visitor passed the test. If not, then we send the error message.

Let’s see our controller again:

<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller
{

  function __construct()
  {
    parent::__construct();
  }

  public function index()
  {
    $this->load->helper('form');
    $this->load->library('form_validation');

    $this->form_validation->set_rules('name','Name','trim|required');
    $this->form_validation->set_rules('g-recaptcha-response','Captcha','callback_recaptcha');

    if($this->form_validation->run()===FALSE)
    {
      $this->load->view('form_view');
    }
    else
    {
      echo 'passed';
    }

  }

  public function recaptcha($str='')
  {
    $google_url="https://www.google.com/recaptcha/api/siteverify";
    $secret='IN_HERE_YOU_PLACE_YOUR_SECRET_KEY_NOT_THE_SITE_KEY';
    $ip=$_SERVER['REMOTE_ADDR'];
    $url=$google_url."?secret=".$secret."&response=".$str."&remoteip=".$ip;
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_TIMEOUT, 10);
    curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.16) Gecko/20110319 Firefox/3.6.16");
    $res = curl_exec($curl);
    curl_close($curl);
    $res= json_decode($res, true);
    //reCaptcha success check
    if($res['success'])
    {
      return TRUE;
    }
    else
    {
      $this->form_validation->set_message('recaptcha', 'The reCAPTCHA field is telling me that you are a robot. Shall we give it another try?');
      return FALSE;
    }
  }
}

Now let’s look again at our form_view.php:

<!doctype html>

<html lang="en">
<head>
<meta charset="utf-8">
<title>reCaptcha form</title>
<script src="https://www.google.com/recaptcha/api.js"></script>
</head>

<body>
  <?php
  echo '<h1>The form</h1>';
  echo form_open();
  echo form_label('Name:','name').'<br />';
  echo form_error('name','<div style="color:red;">','</div>');
  echo form_input('name',set_value('name')).'<br />';
  echo form_error('g-recaptcha-response','<div style="color:red;">','</div>');
  echo '<div class="g-recaptcha" data-sitekey="IN_HERE_YOU_PLACE_YOUR_GOOGLE_SITE_KEY"></div>';
  echo form_submit('submit','Add');
  echo form_close();
  ?>
</body>
</html>

All done. Hope you enjoyed it.

17 comments

  1. Awesome! I’m gonna give it a try on my own website, which is built with CodeIgniter.
    Love all your tutorials by the way. Very clearly explained. Very useful.

  2. Hi,

    Thanks so much for this brilliant article. I have tried it on a linux machine and it works like a charm (CI 3.01).
    However when i host the same on a windows machine (still localhost), It fails. When I var_dump Sres after curl_exec() it says bool (False) and $res after json_decode() is NULL.

    I have curl enabled in php.ini.

    What could be the problem?

    Thanks.

    1. Hello. All I can tell you is about the problem I’ve faced (and couldn’t solve…). I also have a localhost on a windows machine and, even though the curl is enabled, it doesn’t work, because (being my work computer…), everything goes through a proxy which may block these calls.

  3. Unable to access an error message corresponding to your field name

    when trying to use captcha

    public function create_account() {

    $rules = array(

    $this->validation->validation_rule(‘l_fname’,’First Name’,’required’),

    $this->validation->validation_rule(‘l_lname’,’Last Name’,’required’),

    $this->validation->validation_rule(’email’,’Email ID’,’required|valid_email’),

    //$this->validation->validation_rule(‘l_company’,’Name’,’required’),

    $this->validation->validation_rule(‘l_mobile’,’Mobile No’,’required|min_length[10]|max_length[10]|numeric’),

    $this->validation->validation_rule(‘password’,’Password’,’required|alpha_numeric|min_length[8]’),

    $this->validation->validation_rule(‘signup’,”,”)

    ,array(‘field’=>’g-recaptcha-response’,’label’=>’Recaptcha’,’rules’=>’required|callback_getResponse’)

    );
    }

    this is how I am validating

    1. Why make a validation rule without rules? What is “signup”? Why put it in there if you didn’t set up the validation rules?

  4. Hello Avenirer,

    great tuto as usual !
    However I have a question for you.
    My website uses a captcha in different pages. So I tried to put the validation callback function outside of the controller so I can reuse it where I want.
    And that’s the problem… It seems that form_validation doesn’t support having a callback function in a library for example.
    Have you already tried this ?
    Thank you for your help

Leave a Reply

Your email address will not be published.

No spam? * Time limit is exhausted. Please reload CAPTCHA.