Антиспам для Contact Form 7

После того, как очередной плагин по устранению спама в формах Contact Form 7 перестал справляться со своей задачей, я был вынужден изучить этот вопрос более подробно.

Вывод такой: большинство способов защиты Contact Form 7 от спама сводятся к установке CAPTCHA или сторонних сервисов. Но мне оказался ближе более простой подход — Time-Check. Его идея заключается в проверке времени, прошедшего между загрузкой страницы и отправкой формы. Метод практически не влияет на пользователей, совместим с кэшированием страниц и позволяет отсечь значительную часть простых спам-ботов без использования CAPTCHA.

Time-Check

План прост:

  1. В форме есть скрытое поле
  2. Имя этого поле не так важно, но в моем случае — это form_loaded
  3. Через JS мы записываем в это поле текущее время, если форма отправлена слишком быстро или слишком поздно — отправка блокируется
  4. Потом в PHP обработчике мы окончательно проверяем данное поле на заполненность и также проверяем на слишком быструю или позднюю отправку

Звучит слишком просто для антиспам защиты, но главное работает.

Код

В контактную формы вставляем скрытое поле:

[hidden form_loaded id:form_loaded]

В JS пишем Time-Check на стороне клиента:

(function () {
	const MIN_TIME = 5;        // минимальное время заполнения (сек)
	const MAX_TIME = 86400;    // 24 часа

	function setTimestamp() {
		const fields = document.querySelectorAll('input[name="form_loaded"]');

		fields.forEach((field) => {
			field.value = Math.floor(Date.now() / 1000);
		});
	}

	function bindValidation() {
		document.addEventListener('submit', function (e) {
			const form = e.target;

			if (!form.classList.contains('wpcf7-form')) {
				return;
			}

			const field = form.querySelector('input[name="form_loaded"]');

			if (!field || !field.value) {
				e.preventDefault();
				return;
			}

			const now = Math.floor(Date.now() / 1000);
			const diff = now - parseInt(field.value, 10);

			// слишком быстро (бот)
			if (diff < MIN_TIME) {
				e.preventDefault();
				return;
			}

			// слишком старая форма
			if (diff > MAX_TIME) {
				e.preventDefault();
				return;
			}
		});
	}

	document.addEventListener('DOMContentLoaded', function () {
		setTimestamp();
		bindValidation();
	});
})();

В functions.php уже записываем серверную часть проверки Time-Check:

add_filter(
	'wpcf7_validate',
	function ( $result ) {

		$timestamp = isset( $_POST['form_loaded'] )
			? (int) $_POST['form_loaded']
			: 0;

		if ( ! $timestamp ) {
			$result->invalidate( '', 'Spam detected.' );
			return $result;
		}

		$diff = time() - $timestamp;

		if ( $diff < 5 ) {
			$result->invalidate( '', 'Spam detected.' );
			return $result;
		}

		if ( $diff > DAY_IN_SECONDS ) {
			$result->invalidate( '', 'Form expired.' );
		}

		return $result;
	},
	20,
	1
);