Simple CAPTCHA
Image
To protect your forms from bots, you can require users to fill out a Simple Captcha text as seen on a dynamically created image.
Configuration
You need to explicitly enable the CAPTCHA first, and you might want to change how the CAPTCHA looks. This can be done with the captcha.set
config option.
<?php
return [
// enable the captcha check and its API endpoints
'bnomei.kart.captcha.enabled' => true,
// configure how the generated image looks like
'bnomei.kart.captcha.set' => function () {
// https://github.com/S1SYPHOS/php-simple-captcha
$builder = new \Bnomei\Kart\CaptchaBuilder;
$builder->bgColor = '#FFFFFF';
$builder->lineColor = '#FFFFFF';
$builder->textColor = '#000000';
$builder->applyEffects = false;
$builder->build();
kirby()->session()->set('captcha', $builder->phrase);
return [
'captcha' => $builder->inline(),
];
},
// other options...
];
Securing Forms
The plugin does provide a snippet to render the image.
<?php snippet('kart/captcha') ?>
However, the following example shows how to use Alpine.js to render the image dynamically, allowing for manual refreshes.
<form method="POST" action="<?= kart()->urls()->login() ?>">
<label>
<input type="email" name="email" required
placeholder="<?= t('email') ?>" autocomplete="email"
value="<?= urldecode(get('email', '')) ?>">
</label>
<label>
<input type="password" name="password" required
placeholder="<?= t('password') ?>" autocomplete="off">
</label>
<div x-data="{
captcha: undefined,
refresh() {
fetch('<?= kart()->urls()->captcha() ?>')
.then(response => response.json())
.then(data => {
this.captcha = data.captcha;
})
}
}" x-init="refresh()">
<label>
<span>Captcha: </span>
<input name="captcha" type="text" value=""
required pattern="[a-zA-Z0-9]{5}">
</label>
<figure>
<img :src="captcha" width="150" height="40"/>
</figure>
<button x-on:click="refresh()" type="button">Refresh</button>
</div>
<input type="hidden" name="redirect" value="<?= $page?->url() ?>">
<button type="submit" onclick="this.disabled=true;this.form.submit();"><?= t('login') ?></button>
</form>
Behind the Scenes
The endpoints intended for public use, which are most likely targeted by bots (login, register/signup, magic-link), are preconfigured with a check for the Simple Captcha.
If posted with the form, the \Bnomei\Kart\Router::denied()
-helper will query the current request for the captcha
and check whether the form is legit.
Even if you use Captcha, you should keep the CSRF
-check as an additional layer of security.
If the form is successful, it will continue as intended. If not, it will redirect to the error page or return the respective HTTP status code.