Защита сайта на MODX от спама

Форма обратной связи на компоненте Formit в использование обертки AjaxForm

Вызов сниппета (на его месте появится форм обратной связи)


{$_modx->runSnippet("!AjaxForm", [
'snippet' => 'FormIt',
'form' => '@FILE chunks/all_theme_parts/callback/no_modal_form.tpl',
'hooks' => 'spam,email,FormItSaveForm',
'emailTpl' => '@FILE chunks/all_theme_parts/callback/email_form.tpl',
'emailSubject' => 'Заказ звонка с сайта '~$http_host,
'emailTo' => $contact_email_for_orders,
'emailFrom' => 'lead@'~$http_host,
'formName' => 'Отправка формы обратной связи с сайта '~$http_host,
'validate' => 'page:required,phone:required,username:blank',
])}

Верстка формы которую использует сниппет


<form action="{$site_url}{$_modx->resource.id | url}" method="post" id="callbackrow" class="ajax_form">
    <input type="hidden" name="page" value="{$_modx->resource.pagetitle | htmlent}">
    <input type="hidden" name="pageid" value="{$_modx->resource.id}">
    <input type="hidden" name="pageurl" value="{$site_url~$_modx->resource.id | url}">
    <input type="hidden" id="clientID" name="clientID" value="">
    <input type="hidden" name="form_name" value="Запрос обратного звонка">
        <div class="modal-body">           
            <div class="message"></div>
            <div class="fields">
                <div class="form-group hidden">
                    <label >Ваше имя
                    <input type="text" id="callbackform_user" class="form-control" name="username"
                           placeholder="Пользователь">
                    </label>
                </div>
                <div class="form-group pb-2">
                    <input type="text" class="form-control form-control-lg" id="callbackform_name" name="name"
                           placeholder="Ваше имя" required>
                </div>
                <div class="form-group">
                    <input type="text" class="form-control phone form-control-lg" id="callbackform_phone" name="phone"
                           placeholder="Ваш телефон*" required>
                </div>
                                <div class="form-group">
                    <input type="text" class="form-control phone form-control-lg" id="callbackform_email" name="email"
                           placeholder="Ваш e-mail*" required>
                </div>
              
                <div class="form-group">
                    <textarea class="form-control" name="text" id="callbackform_text" placeholder="Ваше сообщение или вопрос" required></textarea>
                </div>
                {/if}
            </div>
        </div>
    <div class="fields">
                <div class="row px-3 mb-3 mt-3">                    
                    <div class="col-12 text-end">
                        <button type="submit" class="btn btn-warning btn-xl mt-3">{$btn_text?:'Отправить'}</button>
                    </div>
                </div>
    </div>
</form>

Как выглядит форма в браузере

Как срабатывает защита от ботов со скрытым полем?

Спам все равно приходит даже со скрытыми полями в MODX и компоненте Formit

Код сниппета.

<?php
$_SESSION['trueuser'] = 1;
return true;

Теперь нужно добавить прехук в вызов нашего сниппета


{$_modx->runSnippet("!AjaxForm", [
'snippet' => 'FormIt',
 'preHooks' => 'checkBotDirect',
'form' => '@FILE chunks/all_theme_parts/callback/no_modal_form.tpl',
'hooks' => 'spam,email,FormItSaveForm',
'emailTpl' => '@FILE chunks/all_theme_parts/callback/email_form.tpl',
'emailSubject' => 'Заказ звонка с сайта '~$http_host,
'emailTo' => $contact_email_for_orders,
'emailFrom' => 'lead@'~$http_host,
'formName' => 'Отправка формы обратной связи с сайта '~$http_host,
'validate' => 'page:required,phone:required,username:blank',
])}

Изменяем файл action.php а папке ajaxForm

<?php
/** @var modX $modx */
define('MODX_API_MODE', true);
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/index.php';
$modx->getService('error', 'error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_ERROR);
$modx->setLogTarget('FILE');

// Switch context if need
if (!empty($_REQUEST['pageId'])) {
    if ($resource = $modx->getObject('modResource', (int)$_REQUEST['pageId'])) {
        if ($resource->get('context_key') != 'web') {
            $modx->switchContext($resource->get('context_key'));
        }
        $modx->resource = $resource;
    }
}

/** @var AjaxForm $AjaxForm */
$AjaxForm = $modx->getService('ajaxform', 'AjaxForm', $modx->getOption('ajaxform_core_path', null,
        $modx->getOption('core_path') . 'components/ajaxform/') . 'model/ajaxform/', array());

//проверяем на бота
if($_SESSION['trueuser'] != 1){ //не прошел проверку
    echo  $AjaxForm->success('Сообщение успешно отправлено.'); //скажем что все ок =)))

    
    //Запишем в логи журнала MODX всех кто попытлся обратиться напрямую к скрипту
    //можно закомментировать или удалить это условие если не нужны логи

    // Определение IP-адреса пользователя, учитывая Cloudflare
    if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $ipAddress = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } elseif (isset($_SERVER['REMOTE_ADDR'])) {
        $ipAddress = $_SERVER['REMOTE_ADDR'];
    } else {
        $ipAddress = 'Unknown IP';
    }

// Логирование ошибки проверки поля username

    $modx->log(xPDO::LOG_LEVEL_ERROR, 'Обращение к action.php напрямую неудачное');
    $modx->log(xPDO::LOG_LEVEL_ERROR, 'IP-адрес: ' . $ipAddress);
    $modx->log(xPDO::LOG_LEVEL_ERROR, print_r($_POST, true));


    die(); //выходим из скрипта т.к. нет переменной в куках
}
unset($_SESSION['trueuser']);


if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {
    $modx->sendRedirect($modx->makeUrl($modx->getOption('site_start'), '', '', 'full'));
} elseif (empty($_REQUEST['af_action'])) {
    echo $AjaxForm->error('af_err_action_ns');
} else {
    echo $AjaxForm->process($_REQUEST['af_action'], array_merge($_FILES, $_REQUEST));
}

@session_write_close();

Итог

Мы используем Cookie

для корректной работы сайта

X