Yii 1.1: recaptcha

reCAPTCHA validator
24 followers

EReCaptchaValidator is a validator which uses reCAPTCHA to validate a CAPTCHA rendered by the EReCaptcha widget.

You need to have valid public and private keys for this to work. You can get them by registering yourself at http://recaptcha.net/

Current version: 1.3

Resources

Documentation

Requirements

  • Yii 1.1 or above

Installation

  • Extract the release file under protected/extensions

Usage

In the view:

<?php echo CHtml::activeLabel($user, 'validacion'); ?>
<?php $this->widget('application.extensions.recaptcha.EReCaptcha', 
   array('model'=>$user, 'attribute'=>'validacion',
         'theme'=>'red', 'language'=>'es_ES', 
         'publicKey'=>'<your public key>')) ?>
<?php echo CHtml::error($user, 'validacion'); ?>

In the model:

<?php
class UserModel extends CFormModel
{
   public $validacion;
 
   public function rules()
   {
      return array(
         array('validacion', 
               'application.extensions.recaptcha.EReCaptchaValidator', 
               'privateKey'=>'<your private key>'),
      );
   }
 
   public function attributeLabels()
   {
      return array(
         'validacion'=>Yii::t('demo', 'Enter both words separated by a space: '),
      );
   }
}

imehesz has noted that for this validator to work with ActiveRecord, you'll need to do something like this:

Controller:

public function actionRegister()
{
  $form = new User();
 
  $form->scenario = 'registerwcaptcha';
  ...
  if($form->validate())
  {
    // and here is the actual HACKY part
    $form->scenario = NULL;
 
    // save user registration
    $form->save();
  }
}

Model:

public function rules()
{
  return array(
                 ...
                 array(
                     'validacion',
                     'application.extensions.recaptcha.EReCaptchaValidator',
                     'privateKey'=> ENVII_CAPTCHA_PRIVATE_KEY, 
                     'on' => 'registerwcaptcha'
                 ),
                 ...
  );
}

The view remains the same.

Change Log

20110926

  • 1.3 - Updated to support SSL. Some clean-up.

20081125

  • 1.1 - Updated to fix BC broken with new CClientScript methods.

Total 18 comments

#15464 report it
thaddeusmt at 2013/11/12 09:31pm
AJAX Validation fix/hack

The problem with the AJAX validation is the the recaptcha API only validates a code once - and when AJAX validation is enabled in Yii the model is usually validated once for the AJAX validation and a second time on the actual form submit action. This second validation will fail, even if it's correct.

To get the AJAX validation working I am caching the captcha API validation response from the AJAX request, so we don't need to re-validate the captcha on the 2nd request.

The trick to making this work securely (to prevent a bot from being able to re-submit the form with the same recaptcha values) is to delete the cached captcha from the user session. I included the code for this at the end of my comment.

Here is the new validateAttribute() method for the EReCaptchaValidator class to cache the recaptcha validation. You can make these modifications as well, or make your own validator that extends EReCaptchaValidator.

protected function validateAttribute($object, $attribute) {
      $valid = false;
      // only validate if the captcha has been POSTed
      if (isset($_POST['recaptcha_challenge_field']) && isset($_POST['recaptcha_response_field'])) {
        // sessions are enabled, so we can cache the value
        if ($session = Yii::app()->session) { 
          // if this question that was asked before, check the cached answer
          if (isset($session['cCache']) && isset($session['cCache']['q'])
            && $session['cCache']['q'] == $_POST['recaptcha_challenge_field']
          ) {
            // if the cached answer equals the provided answer,
            // we know it was previously validated as correct
            if (isset($session['cCache']['a']) && 
                $session['cCache']['a'] == $_POST['recaptcha_response_field']) {
              $valid = true;
            }
          } else { // validate the captcha 
            $resp = recaptcha_check_answer(
              $this->privateKey,
              $_SERVER['REMOTE_ADDR'],
              $_POST['recaptcha_challenge_field'],
              $_POST['recaptcha_response_field']
            );
            // if it validates, store challenge and response hash in session
            if ($resp->is_valid) {
              if (Yii::app()->session) {
                Yii::app()->session['cCache'] = array(
                  'q' => $_POST['recaptcha_challenge_field'],
                  'a' => sha1($_POST['recaptcha_response_field']),
                );
              }
              $valid = true;
            }
          }
        }
      }
      // if the captcha or cached value were not validated, set the error
      if (!$valid) {
        $message = $this->message !== null ? $this->message : Yii::t('yii', 'The verification code is incorrect.');
        $this->addError($object, $attribute, $message);
      }
    }

Then, add this code after the form model has been successfully saved, to prevent a bot from re-submitting the captcha value.

if (Yii::app()->session && isset(Yii::app()->session['cCache'])) {
      // delete the cached captcha values
      unset(Yii::app()->session['cCache']);
    }
#12900 report it
Trejder at 2013/04/19 06:33am
Another notice

Take into consideration, that you should not add your recaptcha field to required validator, as this will fail.

Also, keep in mind, that adding following line:

<?php echo CHtml::error($model, 'captcha'); ?>

below your recaptcha field works only for server-side validation. Even, if you have client-side validation enabled, it will not work this way.

#12838 report it
Trejder at 2013/04/15 09:27am
Notice for users

Extension works like a charm, so many thanks to the creator.

If you decide to replace default verifyCode with this extension, remember to remove some then unecessary elements from your application:

  • call to CCaptchaAction in actions() function in controller responsible for handling your previous bulid-in captcha,
  • remove old verifyCode rule from rules() from your form model as well as public symbol $verifyCode form it,
  • customize your form view, so reCAPTCHA best suits your needs,
  • etc.

Remember, that you can customize look & feel of your captcha -- look into documentation for more info.

#7836 report it
Vedran at 2012/04/21 01:14pm
Very useful extension!

Just want to say thank you for this extension. Good work!

P.S. If someone knows how to make this work with ajax validation it would be good to share with the rest of us.

#7484 report it
jwinn at 2012/03/26 02:15am
Doesn't work with AJAX Validation

The form was successfully submitting as if the reCaptcha was not there at all. Even if it was left blank. I had to set enableAjaxValidation to FALSE and replaced the ajax validation code in my controller action, with the $model->validate() to get it work.

#7132 report it
sobit at 2012/02/26 08:45am
IT'S PERFECT!!!!!!!!!!

THANKS A LOT!!!!!!!

#6730 report it
Jampire at 2012/02/01 12:17pm
Suggestion

I suggest to update setLanguage() in EReCaptcha.php to this:

public function setLanguage($value)
{
$this->language = empty($value) ? 'en' : (($p=strpos($value,'_'))!==false) ? strtolower(substr($value,0,$p)) : strtolower($value);
if (!in_array($this->language, $this->validLanguages)) $this->language = 'en';
}

Then you can put your custom language or set it dynamically by application. And update setTheme() method too to put your custom theme.

public function setTheme($value)
{
$this->theme = $value;
if (!in_array($this->theme, $this->validThemes)) $this->theme = 'red';
}
#5381 report it
Roman Solomatin at 2011/10/08 01:24pm
Excellent!

Thank you.

#5242 report it
omarxp at 2011/09/26 06:51am
thanks

thanks, very helpful.

#5237 report it
Vlad V at 2011/09/25 03:38pm
Add SSL support

I use this extension for a project, a website which runs over SSL.

I know that reCaptcha has support for HTTPS requests so users will not get warnings in browser (like "this page has some resources that are not sent via a secured connection") when loading a page that has reCaptcha.

This is how I got this done:

In EReCaptcha.php, I modified the line ~153 as follows:

echo recaptcha_get_html(Yii::app()->params['recaptcha_public'], null, Yii::app()->params['https']);

You do not need to change the first argument Yii::app()->params['recaptcha_public'] to use ReCaptcha via HTTPS. I did this just to avoid hardcoding it in my application. See this comment.

In configuration, protected/config/main.php, I added

params => array(
[...],
'https'=>true,
[...],
),

And, finally, I updated the reCAPTCHA/recaptchalib.php file. The latest one can be downloaded from here.

#2286 report it
Marcus Matos at 2010/12/07 10:30am
Using yii-user with recaptcha Part 2

Still learning here. I hardcoded the reCaptcha keys in the code with my previous example, but it's better to follow the tips in these comments too: http://www.yiiframework.com/extension/recaptcha/#c1350 http://www.yiiframework.com/extension/recaptcha/#c1383

That is, put the keys in the config. Mind the case sensitivity as the two linked comments have different cases.

#2154 report it
Marcus Matos at 2010/11/18 03:09pm
Using yii-user with recaptcha

If you want to use yii-user with recaptcha, you'll need to do the following (these are all relative to yii-user module root, NOT app root):

in views/user/registration.php, replace:

<?php if (UserModule::doCaptcha('registration')): ?>
    <div class="row">
        <?php echo CHtml::activeLabelEx($form,'verifyCode'); ?>
        <div>
        <?php $this->widget('CCaptcha'); ?>
        <?php echo CHtml::activeTextField($form,'verifyCode'); ?>
        </div>
        <p class="hint"><?php echo UserModule::t("Please enter the letters as they are shown in the image above."); ?>
        <br/><?php echo UserModule::t("Letters are not case-sensitive."); ?></p>
    </div>
    <?php endif; ?>

with

<?php if (UserModule::doCaptcha('registration')): ?>
    <div class="row">
        <?php echo CHtml::activeLabel($form, 'verifyCode'); ?>
        <?php
                $this->widget('application.extensions.recaptcha.EReCaptcha',
                        array('model' => $form, 'attribute' => 'verifyCode',
                            'theme' => 'red', 'language' => 'en_US',
                            'publicKey' => 'PUBLIC KEY  HERE'))
        ?>
    </div>
    <?php endif; ?>

in models/RegistrationForm.php, replace the verifyCode rule with this:

array('verifyCode', 'application.extensions.recaptcha.EReCaptchaValidator', 'privateKey' => 'PRIVATE KEY HERE'),

and very important:

In controllers/RegistrationController.php, around line 45, find this line:

if ($model->save()) {

and change it to:

if ($model->save(false)) {

Here's the reason the last bit is important: The call to the reCaptcha server is only valid ONCE. However, this controller ends up calling it twice, once at line 34 ($model->validate()) and then again at line 45 ($model->save()) because the save() actually calls the validate() again.

Passing false as a parameter to save() will bypass the validation, which is okay because we just did it a few lines earlier.

Hope this saves someone a few hours. :)

#12 report it
dreamynsx at 2010/10/12 10:53am
Need fix for IE compatibility with themes
There is an extraneous comma after {$this->tabIndex}, <---

Please remove it from code since this will cause the theme to not work in IE.

$script =<<theme}', custom_theme_widget : {$customthemewidget}, lang : '{$this->language}', tabindex : {$this->tabIndex} }; EOP;

#1295 report it
imehesz at 2009/09/25 04:42pm
very good

I had a little hick-up at the beginning, but now it works great. I will use this everywhere ;)

--iM

#1350 report it
emix at 2009/08/30 05:33pm
very good

~So1

in order to do so just rewrote two lines in both php files of this extension and replace $this->public/private with Yii::app()->params['reCaptcha']['publicKey'] and Yii::app()->params['reCaptcha']['privateKey'].

It works like a charm now.

#1383 report it
Nemoden at 2009/08/15 01:58am
Great!

Thanks for the extension. I'd like to use it in a little bit different way. I do not want to type keys manually in each recaptcha widget, instead of this I define keys in config:

'params'=>array('recaptcha'=>array(
           
'publicKey'=>'YOUR PUBLIC KEY',
'privateKey'=>'YOUR PRIVATE KEY',
), )
#1736 report it
scythah at 2009/04/23 10:02pm
Excellent

Thank you!

#1831 report it
jasrags at 2009/03/21 11:59am
Works great

Nice interface to recaptcha

Leave a comment

Please to leave your comment.

Create extension