Skip to main content
Version: Fegg 5.x (next)

Introduction

Verifying Google reCAPTCHA / Cloudflare Turnstile responses

Two classes verify the token posted by a CAPTCHA widget with the service's API:

Both send the token with Fegg\Http\Client (cURL when it is installed, otherwise PHP's http stream wrapper; 5 second timeout, 3 seconds to connect).

When the service cannot be reached (timeout, outage, an answer that is not HTTP 200 JSON), auth() fails: success is false and errors has a message asking the visitor to try again later (確認サービスに接続できませんでした。…). A warning is written to the application log. A form protected by a captcha is never accepted unchecked.

How to use​

Import the class and create it with new, passing the secret key. Then pass the posted token to auth().

Controller
<?php
namespace Site\Controller;

defined('FEGG_CORE_DIR') || exit;

use Fegg\Captcha\ReCAPTCHA;
use Fegg\Captcha\Turnstile;
use Fegg\Validation;

class Entry extends Controller
{
public function confirm()
{
$validation = new Validation();

// reCAPTCHA (the secret comes from site/config/env.php)
$recaptcha = new ReCAPTCHA(feggEnv('FEGG_RECAPTCHA_SECRET', ''), 0.5);
$result = $recaptcha->auth($this->in('g-recaptcha-response'));
if (!$result['success']) {
$validation->setError('g-recaptcha-response', implode(',', $result['errors']));
}

// Cloudflare Turnstile
$turnstile = new Turnstile(feggEnv('FEGG_TURNSTILE_SECRET', ''));
$result = $turnstile->auth($this->in('cf-turnstile-response'));
if (!$result['success']) {
$validation->setError('cf-turnstile-response', implode(',', $result['errors']));
}
}
}

Keys​

The keys differ per server, and the secret keys must not be committed. Put them in site/config/env.php (or environment variables) and read them with feggEnv():

htdocs/_fegg/site/config/env.php
'FEGG_RECAPTCHA_SITE_KEY' => '...',
'FEGG_RECAPTCHA_SECRET' => '...',
'FEGG_TURNSTILE_SITE_KEY' => '...',
'FEGG_TURNSTILE_SECRET' => '...',

Use different keys (or the providers' test keys) for development and production. On a test server the keys can go in the committed site/config/env/staging.php (see Test servers deployed with git pull):

Site keySecret
Turnstile, always passes1x00000000000000000000AA1x0000000000000000000000000000000AA
Turnstile, always fails2x00000000000000000000AB2x0000000000000000000000000000000AA

Test secrets accept only the dummy token of the test site keys (Cloudflare); never use them in production. reCAPTCHA v3 has no test keys: register the test server's domain, and set FEGG_RECAPTCHA_SCORE_THRESHOLD to 1.0 to see a rejection. When a captcha is enabled but a key is empty, every form is rejected and an error is logged.

Contact form​

The contact form of the skeleton supports both. Enable one in htdocs/_fegg/site/config/contact/contact_config.php with $_enable_recaptcha or $_enable_turnstile; the file reads the keys from feggEnv():

htdocs/_fegg/site/config/contact/contact_config.php
$_enable_recaptcha = true;
$_recaptcha_key = feggEnv('FEGG_RECAPTCHA_SITE_KEY', '');
$_recaptcha_secret = feggEnv('FEGG_RECAPTCHA_SECRET', '');
$_recaptcha_score_threshold = 0.5;

The widgets are output by site/template/template/antispam.tpl, and site/controller/Contact.php verifies them. Projects whose old contact_config.php has the keys written in it keep working; move them to env.php when convenient.

Methods​