How to set up hCaptcha with CakePHP

4 May 2020

This is a how to secure CakePHP Forms with hCaptcha service.

I’m sure you know what a CAPTCHA is, and even if these can now be solved relatively easily by machine learning [1][2][3], it’s a good way to secure your web forms (e.g. registration/login).

First you need to create a hCaptcha account. You will obtain a sitekey and secret key, which you can store in your CakePHP App Config:

// config/app_local.php
'hCaptcha' => [
    'sitekey' => 'YOUR_SITE_KEY',
    'secret' => 'YOUR_SECRET_KEY',
],

Next you need to include the js lib of hCaptcha in your Layout Template:

// templates/layout/default.php
// in html head or at the end of your body tag:
<script src="https://hcaptcha.com/1/api.js" async defer></script>

In the form you want to secure you include the CAPTCHA and add the sitekey:

// e.g. templates/Articles/add.php
<?= $this->Form->create($article) ?>
<fieldset>
    <legend><?= __('Add Article') ?></legend>
    <?= $this->Form->control('user_id', ['options' => $users]) ?>
    <?= $this->Form->control('title') ?>
    <?= $this->Form->control('body') ?>
    <?= $this->Form->control('published') ?>
    <div class="h-captcha" data-sitekey="<?= Configure::read('hCaptcha.sitekey') ?>"></div>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>

By solving the CAPTCHA and submitting the form, the corresponding controller receives the POST parameter h-captcha-response in the form of a token. This token must send to the hCaptcha API endpoint (https://hcaptcha.com/siteverify) via a POST request to verify the result. For this we use the HTTP Client of CakePHP:

// e.g. src/Controller/ArticlesController.php
use Cake\Core\Configure;
use Cake\Http\Client;

...

public function add()
{
    $article = $this->Articles->newEmptyEntity();
    if ($this->request->is('post')) {
        // Get the token from request data
        $token = $this->request->getData('h-captcha-response');
        // create an httpClient and create a POST request for the API endpoint
        $httpClient = new Client();
        $response = $httpClient->post('https://hcaptcha.com/siteverify', [
            'secret' => Configure::read('hCaptcha.secret'),
            'response' => $token,
        ]);
        // Get the response data as JSON
        $hCaptchaResult = $response->getJson();

        // Check if the result is *success* and save your article/go on wih your business logic
        if ($hCaptchaResult['success']) {
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            if ($this->Articles->save($article)) {
                $this->Flash->success(__('The article has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
        } else {
          // hCaptcha API returns error codes, f.e. if the CAPTCHA isn't filled out or not solved.
          // Check out the error codes here: https://docs.hcaptcha.com/#server
        }

        $this->Flash->error(__('The article could not be saved. Please, try again.'));
    }
    $users = $this->Articles->Users->find('list', ['limit' => 200]);
    $this->set(compact('article', 'users'));
}

If you want to use hCaptcha API in multiple Controllers, you can put the Controllercode in a Component.


  1. Solving CAPTCHAs — Machine learning vs online services
  2. Breaking CAPTCHA Using Machine Learning in 0.05 Seconds
  3. How to break a CAPTCHA system in 15 minutes with Machine Learning
  4. hCaptcha Docs