Google Recaptcha in FormBuilder Guide
For a client I had to add ReCaptcha as we noticed an increase in spam. The Akismet protection only works for blog comments and the hidden checkbox defense did not work sufficiently. We couldn't really find a module for this (or we forgot how to Google?).
First we wanted to go with the extending the FormBuilder as we also upgraded @jdesloovere's file plugin to work with Fork 3.9. Unfortunately this was too much of a hassle with the dependency on the legacy Spoon Framework. I opted to 'hack' in the the ReCaptcha for now as we will do a major overhaul of the website once Twig support is stable.
It turned out to be fairly simple.
Please note that this guide is more of a hack and not really an extension. Furthermore we didn't use *phpcs to validate the code as this will not go into the contributions.*
0. Dependencies
- Fork CMS 3.9 (might work in previous versions as well but untested)
- cURL and php_curl
1. Sign up for Google ReCaptcha
Go to https://www.google.com/recaptcha and register a new website.
2. Add the javascript to your head.tpl file
Add this snipped just before you close the header using the tag.
<script src='https://www.google.com/recaptcha/api.js'></script>
3. Edit your form
Go to our form and add a paragraph item to your form where you would like to have your ReCaptcha appear.
<div class="g-recaptcha" data-sitekey="[public-site-key from step 1]">Please verify your enquiry.</div>
4. Add your private ReCaptcha key and verify URI
In app/config/parameters.yml
add the following:
recaptcha.api_key: [secret]
recaptcha.verify_url: https://www.google.com/recaptcha/api/siteverify
If this parameter works in dev but not in prod don't forget to clear your cache.
5. Edit Frontend\Modules\FormBuilder\Widgets\Form
Because we also have forms without the ReCaptcha we had to add the if statement. Based on the spam attack vector they could just not include the POST parameter and it could potentially fail. If you have ReCaptchas in all your forms you can adapt the above to always validate the post parameter. Another option is to check by form name or id but this implies you need to edit the code below every time you add a new form. We did notice an decrease in spam using the method below.
Before
// validate fields
Add
// get the request object
$request = Request::createFromGlobals();
array_key_exists('g-recaptcha-response', $request->request->all()) ? $recaptcha = true : $recaptcha = false;
// we added recaptcha in this form so need to validate
if($recaptcha) {
$status = $this->recaptcha($request->request->get('g-recaptcha-response'));
if(!$status) {
$this->frm->addError(FL::err('FormReCaptcha'));
}
}
This implies that use have the Symfony Request object in your use statements.
Before the end of the class add the following private functions.
/**
* @return string
*/
private function getIp()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
/**
* @return bool
*/
private function recaptcha($response)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->getContainer()->getParameter('recaptcha.verify_url'));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
http_build_query(
array(
'secret' => $this->getContainer()->getParameter('recaptcha.api_key'),
'response' => $response,
'remoteip' => $this->getIp()
)
)
);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
// successful cURL request to Google's Recaptcha and extract the verification result
if($server_output) {
return json_decode($server_output)->success;
}
// We couldn't perform the cURL request
return false;
}
6. Add the error translation message
Go to your translations and add an error message with FormReCaptcha
as key.
Select Frontend and Error from the dropdowns.
Any feedback is appreciated!