Uniform: Incorrect Captcha value still allowing form submission

Hi there

I’ve just launched my first Kirby site (having built almost exclusively in WordPress for the last 10 years) and I have to say that I think it’s great. It’s a pretty steep learning curve but I’m getting there!

Anyway, having put the site live, the contact forms I have on various pages are being hit by spam bots. I did have the honeypot protection in place at launch but that is pretty much doing nothing [EDIT: This is because I failed to trigger the Honeypot check in the controller - my bad]. I have followed the instructions to add the extra protection and it seemed pretty effective. I added the rule into the controller to make it a required field. However, it would appear that even when an incorrect value is entered the form still submits. I have tried with the default setup (as outlined in the documentation) as well as my tweaked code. Does anyone have any ideas? Here is my template code:

<form action="<?php echo $page->url() ?>#contactform" method="POST" id="contactform">
	<fieldset class="fieldset">
		<legend>Send us an e-mail</legend>
		<div class="grid-container">
			<div class="grid-x grid-margin-x">
				<div class="cell large-6">
					<label for="name">Name <span class="req">*</span></label>
					<input<?php if ($contactform->error('name')): ?> class="error"<?php endif; ?> name="name" id="name" type="text" placeholder="Your name" value="<?php echo $contactform->old('name') ?>">
					<label for="company">Company</label>
					<input<?php if ($contactform->error('company')): ?> class="error"<?php endif; ?> name="company" id="company" type="text" placeholder="Your company name" value="<?php echo $contactform->old('company') ?>">
				</div>
				<div class="cell large-6">									
					<label for="email">E-mail <span class="req">*</span></label>
					<input<?php if ($contactform->error('email')): ?> class="error"<?php endif; ?> name="email" id="email" type="email" placeholder="Your e-mail address" value="<?php echo $contactform->old('email') ?>">
					<label for="tel">Telephone</label>
					<input<?php if ($contactform->error('tel')): ?> class="error"<?php endif; ?> name="tel" id="tel" type="tel" placeholder="The best number to get you on" value="<?php echo $contactform->old('tel') ?>">
					<?php echo honeypot_field('website','contact-website-hp') ?>
				</div>
				<div class="cell">
					<label for="message">Message <span class="req">*</span></label>
					<textarea<?php if ($contactform->error('message')): ?> class="error"<?php endif; ?> name="message" id="message" placeholder="Your message to us..."><?php echo $contactform->old('message') ?></textarea>
				</div>
				<div class="cell">
					<?php echo csrf_field() ?>
					<input type="hidden" name="submittedfrom" value="<?= $page->url() ?>">
					<input type="hidden" name="formid" value="contact">
					<label for="as-result" class="label-asf">What is <?php echo uniform_captcha() ?>? <span class="req">*</span></label>
					<?php echo captcha_field('as-result', 'field-asf'); ?>
					<p class="smaller-text">This field is an anti-spam measure.</p>
					<input type="submit" class="button" value="Submit">
				</div>
			</div>
			<?php if ($contactform->success()): ?>
			<div class="uniform-success">
				Thank you for your message. We will get back to you soon!
			</div>
			<?php else: ?>
				<?php snippet('uniform/errors', ['form' => $contactform]) ?>
			<?php endif; ?>
		</div>
	</fieldset>
</form>

This renders the following HTML:

<form action="contact-us#contactform" method="POST" id="contactform">
	<fieldset class="fieldset">
		<legend>Send us an e-mail</legend>
		<div class="grid-container">
			<div class="grid-x grid-margin-x">
				<div class="cell large-6">
					<label for="name">Name <span class="req">*</span></label>
					<input name="name" id="name" type="text" placeholder="Your name" value="">
					<label for="company">Company</label>
					<input name="company" id="company" type="text" placeholder="Your company name" value="">
				</div>
				<div class="cell large-6">									
					<label for="email">E-mail <span class="req">*</span></label>
					<input name="email" id="email" type="email" placeholder="Your e-mail address" value="">
					<label for="tel">Telephone</label>
					<input name="tel" id="tel" type="tel" placeholder="The best number to get you on" value="">
					<input type="text" name="website" class="contact-website-hp" tabindex="-1" autocomplete="off">
				</div>
				<div class="cell">
					<label for="message">Message <span class="req">*</span></label>
					<textarea name="message" id="message" placeholder="Your message to us..."></textarea>
				</div>
				<div class="cell">
					<input type="hidden" name="csrf_token" value="0ad59f785a8ba6cb119825fb343b807be5a9f271e84e08d9c2b2eca4d6b8ad7b">								<input type="hidden" name="submittedfrom" value="contact-us">
					<input type="hidden" name="formid" value="contact">
					<label for="as-result" class="label-asf">What is 9 plus 1? <span class="req">*</span></label>
					<input type="number" name="as-result" class="field-asf">
					<p class="smaller-text">This field is an anti-spam measure.</p>
					<input type="submit" class="button" value="Submit">
				</div>
			</div>
		</div>
	</fieldset>
</form>

The controller contains the following code:

$contactform = new Form([
	'name' => [
		'rules' => ['required'],
		'message' => 'Please enter your name',
	],
	'email' => [
		'rules' => ['required', 'email'],
		'message' => 'Please enter a valid email address',
	],
	'company' => [],
	'tel' => [],
	'message' => [
		'rules' => ['required'],
		'message' => 'Please enter a message',
	],
	'submittedfrom' => [],
	'as-result' => [
		'rules' => ['required', 'num'],
		'message' => 'Please fill in the anti-spam field',
	],
],'contact-form');

When I submit the form, I get an error message if I have left the captcha field empty but if I put the wrong value in the form still gets submitted.

I suspect I’ve simply overlooked something really simple but I can’t see what it might be.

Thanks in advance for any help!

The $form->calcGuard(['field' => 'result']); is missing from your controller, it seems. See the documentation.

See? I knew it would be something obvious.

Of course, this also means that I was missing the honeypot from the controller too.

So if I understand correctly, my controller (I had omitted the actual sending part - sorry) should be something like this (to use both the honeypot and the Calc):

use Uniform\Form;

return function ($kirby)
{
    $contactform = new Form([
        'name' => [
			'rules' => ['required'],
            'message' => 'Please enter your name',
		],
		'email' => [
            'rules' => ['required', 'email'],
            'message' => 'Please enter a valid email address',
        ],
		'company' => [],
		'tel' => [],
        'message' => [
            'rules' => ['required'],
            'message' => 'Please enter a message',
        ],
		'submittedfrom' => [],
		'as-result' => [
			'rules' => ['required', 'num'],
			'message' => 'Please fill in the anti-spam field',
		],
    ],'contact-form');

    if ($kirby->request()->is('POST')) {
		
		$contactform->honeypotGuard(['website' => 'url']);
		$contactform->calcGuard(['as-result' => 'result']);
		
		$contactform->emailAction([
			'to' => 'hello@email.com',
			'from' => 'admin@email.com',
			'subject' => 'Contact form submission from {{name}} via website.com',
			'template' => 'contact'
		]);
    }

    return compact('contactform');
};

Is that correct? Part of me feels that they should be nested in some way, but then I’m not really a developer.

You can also write it like this:

	$contactform
         ->honeypotGuard(['website' => 'url'])
         ->calcGuard(['as-result' => 'result'])
         ->emailAction([
			'to' => 'hello@email.com',
			'from' => 'admin@email.com',
			'subject' => 'Contact form submission from {{name}} via website.com',
			'template' => 'contact'
		]);

Thanks @texnixe - that is a great help.

I now have the opposite problem! When I try and submit the form with the CORRECT value in the calc field, I get the error message “Please solve the arithmetic problem”

Note: The error checking for it being a required field still works so leaving it blank elicits the correct “Please fill in…” response)

I have checked field names etc but can’t see anything.

Just to recap, the field is called in the template:

<?php echo captcha_field('as-result', 'field-asf'); ?>

This generates:

<input type="number" name="as-result" class="field-asf">

It is checked as being completed in the controller:

$contactform = new Form([
	...
	'as-result' => [
		'rules' => ['required', 'num'],
		'message' => 'Please fill in the anti-spam field',
	],
],'contact-form');

Then the controller calls the verification:

if ($kirby->request()->is('POST')) {
		$contactform
		->honeypotGuard(['website' => 'url'])
		->calcGuard(['as-result' => 'result'])
		->emailAction([
			...
		]);
    }

Any ideas?

Should be

$contactform->calcGuard(['field' => 'as-result']);

Hmm, I’ve corrected it but I’m still getting the same error.

I’ve tried reverting the code back to the default values as outlined in the documentation but still no joy. When I have time I’ll try building a new, standalone form to test it but for now this is unsolved!

On the plus side, the honeypot is now working [sheepish look]

Hm, I haven’t used the Uniform plugin in a while and never with the captcha. Maybe @mzur has an idea? If not and if I have time, I’ll try to look into this.

Initial issue has been solved but the new issue (detailed from Post 5 onwards) has been opened in a new post, so as to avoid confusion.