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.
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.
Thanks sir. it’s help me a lot.
Perfect solution. Very well explained. Thank you.
Russ
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.
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.
Hi,
Ive done everything there but I am facing a problem.
$url=$google_url.”?secret=”.$secret.”&response=”.$str.”&remoteip=”.$ip;
after this line, to check the values I “echo” the variable $url. and the result it is showing that the response is EMPTY. There is nothing. Like this. Can you please tell me why am I facing this?
https://www.google.com/recaptcha/api/siteverify?secret=************************&response=&remoteip=::1
Did you use the secret key on the domain you took the key for?
I don’t normally comment but this was awesome. thank you!
Thank you 🙂
Great tutorial!
Works fine in my test site.
thank you 🙂
You are more than welcome.
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
Why make a validation rule without rules? What is “signup”? Why put it in there if you didn’t set up the validation rules?
Awesome bro!!! Thanx!
Excellent tutorial !!
To get it to work on localhost I added,
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
see http://stackoverflow.com/questions/8419747/php-curl-does-not-work-on-localhost
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
I am sorry. I never encountered such a problem. Maybe if you give me more details…