Skip Navigation

[Resolved] Problems implementing custom validation on cred form to prevent bot spam

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
- 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 -
- 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 -

Supporter timezone: Asia/Kolkata (GMT+05:30)

This topic contains 7 replies, has 3 voices.

Last updated by Minesh 1 year, 1 month ago.

Assisted by: Minesh.

Author
Posts
#2649285
spam entry.png

Tell us what you are trying to do? Trying to prevent bot spam by means of a custom validation function

Is there any documentation that you are following? Yes, https://toolset.com/documentation/programmer-reference/cred-api/

Is there a similar example that we can see? The problem is in the form entries by bots, you see an example in the attached image

What is the link to your site? hidden link

hidden link is part of a multisite on hidden link, running job boards. On one of the public accessible cred forms, 'solliciteer', people can apply for an offered job by filling in a post form. Lately we get a lot of spam in russion type and with .ru domains in email addresses and urls.

This is the post form:
[credform class='cred-form cred-keep-original']

[cred_field field='form_messages' value='' class='alert alert-warning']

<div style="display: none;">

<h3>Vacature:</h3>
[cred_field field='vacature' post='sollicitatie' value='[wpv-post-title]' urlparam='' readonly='true' required='true']
<h3>Vestiging:</h3>
[cred_field field='cred-vestiging' post='sollicitatie' value='[types field='vestiging'][/types]' urlparam='' readonly='true']
</div>
<h3>Je naam:</h3>
[cred_field field='post_title' post='sollicitatie' value='' urlparam='']
<h3>Je e-mailadres:</h3>
[cred_field field='e-mail' post='sollicitatie' value='' urlparam='']
<h3>Je telefoonnummer:</h3>
[cred_field field='telefoon' post='sollicitatie' value='' urlparam='']
<h3>Je LinkedIn profiel:</h3>
[cred_field field='linkedin-profiel' post='sollicitatie' value='' urlparam='' class='linkedin']

<h3>Je CV:</h3>
Upload je cv via e-mail<br>
<div style="font-size: 10px;">Uit veiligheidsoverwegingen en wegens AVG-voorschriften bewaren wij cv's niet op de webserver maar verzoeken wij sollicitanten deze rechtstreeks te mailen. Toegestane bestandstypes: jpg jpeg png gif mp3 mp4 pdf doc docx zip<br>
maximale bestandsgrootte: 3MB</div>
<!-- [cred_field field='cv' post='sollicitatie' value='' urlparam='' output='bootstrap'] -->

<h3>Je sollicitatie op vacature [wpv-post-title]:</h3>
[cred_field field='sollicitatietekst' post='sollicitatie' value='' urlparam='']

<br>

[cred_field field='form_submit' value='Insturen' urlparam='']
[/credform]

I show one of the typical bot spam entries in the attached images.

We tried the following steps to prevent this:

1. We made the hidden field wpcf-vacature a required field, both in the custom field sdettings and in the post form code. This did not prevent spam entries where this field was blank to be submitted to the database.

2. A CAPTCHA field that was already present in the form (I am not a robot). This didn't prevent the spam entries from being submitted.

3. A javascript validation added to the form in the cred post form editor. This script was checking various fields on the .ru string and also a field that is hidden on the front end by a display: none css rule, and that is being autofilled with data from the post where the form is displayed. In the spam entries this field was empty, which means the bots use another route to access the form than a common web browser, and they don't seem to see this hidden field.

jQuery(document).ready(function($) {
// Target the CRED form with ID 20, replace 'cred-form-20' with your actual CRED form's ID.
$('#cred-form-20').on('submit', function(event) {
// Get the values of the fields 'wpcf-linkedin-profiel', 'wpcf-sollicitatietekst', and 'wpcf-e-mail'.
var linkedinField = $('#wpcf-linkedin-profiel');
var sollicitatietekstField = $('#wpcf-sollicitatietekst');
var emailField = $('#wpcf-e-mail');

// Check if any of the fields contain the ".ru" string.
if (
(linkedinField.length > 0 && linkedinField.val().includes('.ru')) ||
(sollicitatietekstField.length > 0 && sollicitatietekstField.val().includes('.ru')) ||
(emailField.length > 0 && emailField.val().includes('.ru'))
) {
// If the ".ru" string is found in any of these fields, prevent the form from submitting.
event.preventDefault();
}

// Get the value of the hidden field 'wpcf-vacature' and check if it exists.
var vacatureField = $('#wpcf-vacature');

if (vacatureField.length === 0 || (vacatureField.length === 1 && vacatureField.val() === '')) {
// If the 'wpcf-vacature' field does not exist or if its value is empty, prevent the form from submitting.
event.preventDefault();
}
});
});

The javascript validation did not prevent the form data fdrom being submitted. That's why we concluded the spam bots must use a way to access the form that is not using javascript.

4. A custom validation function using the cred_form_validate API hook:

/**
* Custom field validation using the cred_form_validate hook.
*
* @param array $field_validation_results An array of field validation results.
* @param array $form_data The form data.
*
* @return array Modified field validation results.
*/
function custom_field_validation($field_validation_results, $form_data) {
// Check if the form is the one you want to validate (replace 20 with your form ID).
if ($form_data['id'] === 20) {
// Get the values of the fields you want to validate.
$vacatureFieldValue = isset($_POST['wpcf-vacature']) ? sanitize_text_field($_POST['wpcf-vacature']) : '';
$linkedinFieldValue = isset($_POST['wpcf-linkedin-profiel']) ? sanitize_text_field($_POST['wpcf-linkedin-profiel']) : '';
$sollicitatietekstFieldValue = isset($_POST['wpcf-sollicitatietekst']) ? sanitize_text_field($_POST['wpcf-sollicitatietekst']) : '';
$emailFieldValue = isset($_POST['wpcf-e-mail']) ? sanitize_email($_POST['wpcf-e-mail']) : '';

// Check if the ".ru" string is present in any of the specified fields.
if (
strpos($linkedinFieldValue, '.ru') !== false ||
strpos($sollicitatietekstFieldValue, '.ru') !== false ||
strpos($emailFieldValue, '.ru') !== false
) {
// If the ".ru" string is found, add an error message to the field.
$field_validation_results['wpcf-linkedin-profiel'] = 'Invalid input';
$field_validation_results['wpcf-sollicitatietekst'] = 'Invalid input';
$field_validation_results['wpcf-e-mail'] = 'Invalid input';

// Return an empty field validation results array to prevent saving.
error_log('Validation failed due to ".ru" string');
return array();
}

// Check for the presence of the 'wpcf-vacature' field.
if (empty($vacatureFieldValue)) {
// If the 'wpcf-vacature' field is empty, add an error message to the field.
$field_validation_results['wpcf-vacature'] = 'This field is required';

// Return an empty field validation results array to prevent saving.
error_log('Validation failed due to empty "wpcf-vacature" field');
return array();
}
}

// Return the modified field validation results.
return $field_validation_results;
}

// Hook the custom validation function to the cred_form_validate hook.
add_filter('cred_form_validate', 'custom_field_validation', 10, 2);

This function was added to the site in a custom plugin. In order to make this work we had to remove the CAPTCHA as this interfered with it's javascript. We could see in the debug.log that errors were reported when we filled in the form with two of the tested fields containing a .ru substring. However the form data was being submitted to the database and no errors were presented on the front end. I paste the relevant lines from debug.log here:

[02-Oct-2023 11:36:06 UTC] Validation failed due to ".ru" string
[02-Oct-2023 11:36:06 UTC] PHP Notice: Undefined offset: 0 in /home/qjobs/public_html/wp-content/plugins/cred-frontend-editor/application/controllers/validators/base_custom_validation_error_message_handler.php on line 47
[02-Oct-2023 11:36:06 UTC] PHP Notice: Undefined offset: 1 in /home/qjobs/public_html/wp-content/plugins/cred-frontend-editor/application/controllers/validators/base_custom_validation_error_message_handler.php on line 47

So here we are. We want to prevent this spam by a routine that intercepts it before saving the form data and prevents it from saving the form data. Interception works, but preventing from saving doesn't. What are we overlooking here or doing wrong?

#2649349

Nigel
Supporter

Languages: English (English ) Spanish (Español )

Timezone: Europe/London (GMT+00:00)

Hi there

The server-based validation via the cred_form_validate filter should be the most dependable solution.

I'm not sure where you got the idea to return an empty array when the validation fails, that might be why the form is completing submission even if your tests identify the problem .ru string.

Please check the examples for the filter in our documentation, and be sure to return an array of fields and errors.

I expect that should make your server-based validation work correctly.

#2649759

Thank you Nigel,

I must confess I had used ChatGPT to help me write the function, but it appears their knowledge of the Toolset Forms API is not as good as they claim it is.

I rewrote the function based on the first example and now it works.
As a reference for other users I paste my solution here. I wish I knew how to use syntax highlighting here in the support threads.

/**
* Custom field validation using the cred_form_validate hook.
*
* @param array $errors An array of field validation results.
* @param array $form_data The form data.
*
* @return array Modified field validation results.
*/
function custom_field_validation($error_fields, $form_data) {
//field data are field values and errors
list($fields,$errors)=$error_fields;
// Check if the form is the one you want to validate (replace 20 with your form ID).
if ($form_data['id'] == 20) {
// Get the values of the fields you want to validate.
$vacatureFieldValue = isset($_POST['wpcf-vacature']) ? sanitize_text_field($_POST['wpcf-vacature']) : '';
$linkedinFieldValue = isset($_POST['wpcf-linkedin-profiel']) ? sanitize_text_field($_POST['wpcf-linkedin-profiel']) : '';
$sollicitatietekstFieldValue = isset($_POST['wpcf-sollicitatietekst']) ? sanitize_text_field($_POST['wpcf-sollicitatietekst']) : '';
$emailFieldValue = isset($_POST['wpcf-e-mail']) ? sanitize_email($_POST['wpcf-e-mail']) : '';

// Check if the ".ru" string is present in any of the specified fields.
if (
strpos($linkedinFieldValue, '.ru') !== false
) {
// If the ".ru" string is found, add an error message to the field.
$errors['wpcf-linkedin-profiel']='Wrong Value';
// Return an empty field validation results array to prevent saving.
error_log('Validation wpcf-linkedin-field failed due to ".ru" string');
}
if (
strpos($sollicitatietekstFieldValue, '.ru') !== false
) {
// If the ".ru" string is found, add an error message to the field.
$errors['wpcf-sollicitatietekst']='Wrong Value';
// Return an empty field validation results array to prevent saving.
error_log('Validation wpcf-solliciotatietekst failed due to ".ru" string');
}
if (
strpos($emailFieldValue, '.ru') !== false
) {
// If the ".ru" string is found, add an error message to the field.
$errors['wpcf-e-mail']='Wrong Value';
// Return an empty field validation results array to prevent saving.
error_log('Validation wpcf-e-mail failed due to ".ru" string');
}

// Check for the presence of the 'wpcf-vacature' field.
if (empty($vacatureFieldValue)) {
// If the 'wpcf-vacature' field is empty, add an error message to the field.
$errors['wpcf-vacature'] = 'This field is required';

// Return an empty field validation results array to prevent saving.
error_log('Validation wpcf-vacature failed due to empty "wpcf-vacature" field');
}
}

// Return the modified field validation results.
return array($fields,$errors);
}

// Hook the custom validation function to the cred_form_validate hook.
add_filter('cred_form_validate', 'custom_field_validation', 10, 2);

#2649763

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

Glad to know that following doc and Nigel's reply help you to resolve your issue.

I suggest you should always follow the official doc or reference tickets rather using ChatGTP.

#2649765

Thanks Minesh,

Could you tell me if I can use a syntax highlighter if I post code here in the support threads?

#2649793

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

You can use the code html tag to highlight the code:
- hidden link

#2655755

Hi, there is a security issue with the code I provided in my opening mail in this support thread. The code contains a mailto link with an email addres. I would ask you to remove this or make it invisible as the link shows up in Google results. Thanks in advance, Erik

#2655761

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

I've removed the mailto email address from your first post. Please let me know if you need to remove anything else.