Strona główna → Wtyczki → Antyspam w formularzu Bricksa
Strona publicznej przychodni i zwykły formularz kontaktowy zbudowany w Bricksie, który trzeba było zabezpieczyć przed botami. Warunki były dwa: żadnych usług zewnętrznych, bo dane pacjentów nie mają wychodzić poza serwer, i żadnych zagadek do rozwiązania, bo strona ma spełniać WCAG 2.1 AA. Skończyło się na małym mu-pluginie: honeypot, pułapka czasowa i ciche udawanie sukcesu. Tu jest to, czego dowiedzieliśmy się po drodze z kodu Bricksa.
Nic nie wychodzi poza serwer. Żadnego skryptu z cudzej domeny, żadnych kluczy API, żadnych ciasteczek. Ocena, czy zgłoszenie jest od człowieka, zapada w całości w WordPressie.
Odwiedzający niczego nie rozwiązuje. Nie klika w obrazki i nie przepisuje znaków. Dla osoby z czytnikiem ekranu formularz wygląda dokładnie tak jak przedtem.
Działa na każdym formularzu Bricksa. Także na tych, które pojawią się później — w popupie, w panelu wysuwanym z boku, na nowej podstronie — bez pamiętania o dodaniu pola.
Bot nie dowiaduje się, że wpadł. Dostaje ten sam komunikat „wysłano”, co człowiek. Wiadomość nie idzie nigdzie, a on nie ma powodu, żeby zmieniać taktykę.
Od wersji 1.12.2 każde pole formularza można oznaczyć jako honeypot, a w sekcji ochrony przed spamem są Turnstile, reCAPTCHA i hCaptcha. Wbudowany honeypot warto włączyć i w wielu przypadkach wystarczy. Tutaj potrzebna była druga warstwa — pułapka czasowa — oraz to, żeby ochrona obejmowała każdy formularz automatycznie, bez edycji każdego z osobna w builderze.
Projekt był gotowy przed pierwszą linią PHP. Dwa jego założenia zmieniły się po przeczytaniu, jak Bricks przyjmuje zgłoszenie formularza — i dobrze, że stało się to przed wdrożeniem, a nie po.
Naturalny pomysł to nazwać ukryte pola tak jak pola formularza, z przedrostkiem form-field-, żeby na pewno trafiły do wysyłki. Bricks ma jednak zabezpieczenie: każde pole z tym przedrostkiem, którego nie ma w zapisanych ustawieniach formularza, powoduje odrzucenie całego zgłoszenia jako podejrzanego. Filtr PHP w czasie działania tego nie obejdzie, bo serwer sprawdza zapis z bazy. Nasze pola mają więc własne nazwy. Do wysyłki i tak trafiają, bo front Bricksa zbiera cały formularz, a zabezpieczenie po prostu ich nie dotyczy.
Pułapka czasowa sprawdza, ile minęło od wyświetlenia strony do wysłania. Znacznik czasu wypisany przez PHP zostałby zapisany w cache'u strony razem z resztą HTML-a, a wtedy pułapka po chwili przepuszczałaby każdego — boty też. Dlatego liczy przeglądarka: skrypt zapamiętuje moment startu i przy wysyłce wpisuje liczbę sekund. Bot, który wysyła żądanie z pominięciem JavaScriptu, nie ma tej wartości wcale — i to też jest sygnał.
bricks/form/validate to filtr, nie akcja: dostaje tablicę błędów i obiekt formularza, zwraca tablicę błędów. Wywoływany jest po sprawdzeniu nonce, wbudowanego honeypota i listy pól, a przed walidacją pól wymaganych i przed akcjami — wysłaniem maila czy przekierowaniem. To dokładnie ten moment, w którym trzeba zdecydować, zanim cokolwiek wyjdzie z serwera.
Front Bricksa pokazuje komunikat sukcesu tylko wtedy, gdy odpowiedź ma success: true i data.type równe success. Po wykryciu spamu wysyłamy właśnie taką odpowiedź z komunikatem wziętym z ustawień formularza i kończymy żądanie — akcje formularza w ogóle się nie uruchamiają. Wbudowany honeypot domyślnie odpowiada błędem; da się to zmienić filtrem bricks/element/form/honeypot/result.
add_filter( 'bricks/form/validate', function ( $errors, $form ) {
$hp = sanitize_text_field( wp_unslash( $_POST['zz_hp'] ?? '' ) );
$t = sanitize_text_field( wp_unslash( $_POST['zz_t'] ?? '' ) );
$spam = '' !== trim( $hp ) // honeypot wypełniony
|| ! is_numeric( $t ) // brak czasu = brak JS
|| (int) $t < 3; // za szybko jak na człowieka
if ( ! $spam ) {
return $errors; // człowiek: Bricks robi resztę
}
$msg = $form->get_settings()['successMessage'] ?? 'Dziękujemy.';
wp_send_json_success( [ 'type' => 'success', 'message' => $msg ] );
}, 10, 2 );
Antyspam, który zatrzymuje boty, a przy okazji raz na jakiś czas prawdziwą wiadomość, jest gorszy niż spam. Dlatego te trzy rzeczy były sprawdzane w prawdziwej przeglądarce, na kopii roboczej strony.
Formularz w popupie. Skrypt, który raz po załadowaniu strony dokłada pola do formularzy, nie widzi tych, które pojawią się później. Taki formularz wysłałby się bez pola czasu, a serwer uznałby człowieka za bota. Obserwator zmian w dokumencie dokłada pola także do formularzy dodanych po starcie strony.
Ukrycie honeypota. Pole ma zniknąć dla oka, dla klawiatury i dla czytnika ekranu, ale zostać w formularzu. Jest odsunięte poza ekran, wyjęte z kolejności tabulatora, ukryte przed technologiami asystującymi i ma wyłączone autouzupełnianie — żeby przeglądarka nie wpisała do niego czegoś w imieniu człowieka.
Cztery scenariusze na jednym formularzu. Wysyłka jak człowiek — mail doszedł. Wypełniony honeypot, wysyłka po sekundzie i żądanie bez JavaScriptu — za każdym razem komunikat „wysłano” i żadnego maila. Próg czasu to trzy sekundy i można go zmienić jednym filtrem, bez ruszania kodu.
Napisz, na ilu formularzach i czy strona ma ograniczenia co do usług zewnętrznych. Powiemy, czy wystarczy wbudowany honeypot, czy warto dołożyć drugą warstwę.
Opisz swój formularz →Albo mailem: info@asymetria.com.pl