Nieznośne boty a logowanie

Jakiś czas temu zostałem zapytany, jak zabezpieczać się przed atakami botów. Chodziło konkretnie o notorycznie nękany przez nie skrypt logowania, istota problemu związana jest jednak nie tylko z nim, dlatego postanowiłem podsumować kilka przydatnych faktów.

Przede wszystkim, wszelkie formularze powinny być uczulone na odpalanie ich z zewnątrz. Sprawdzenie zmiennej $_SERVER[‘HTTP_REFERER’] przy weryfikacji danych pochodzących z formularzy powinno być jednym z pierwszych naszych kroków. Chodzi bowiem o to, aby właściwość ta była zawsze niepusta i równa adresowi strony, z której wysyłany jest formularz:

if (isset($_SERVER['HTTP_REFERER']) && 'domena.com/login' == str_replace(array('http://', 'www.', 'https://'), '', $_SERVER['HTTP_REFERER'])) { echo 'Wszystko gra'; };

Podobny kod odsieje domorosłych autorów botów i skryptów “hakujących co się da!!!!1111jeden’, nie załatwi jednak na poważnie problemu, bowiem nagłówek referer można łatwo zmodyfikować przy requeście HTTP. Idziemy dalej.

Nim dopuścimy do porównania danych zawartych w bazie z tymi z formularza, sprawdźmy, czy dany, potencjalny bot obsługuje system sesji. Zakładając, że mamy stronę login.php (zawierająca formularz), stwórzmy zmienną sesji i zapiszmy ją w ukrytym elemencie formularza:

$_SESSION['hidden_value'] = rand(0, 100);
<input type="hidden" name="something" value="<?=$_SESSION['hidden_value']?>" />

Sprawdźmy teraz, po wysłaniu danych formularza, czy zmienne sesyjne będą równać się danym z tablicy $_POST:

if ($_SESSION['hidden_value'] == $_POST['something']) { echo 'Droga wolna'; }

Ukryte elementy formularza można zastosować także nieco inaczej. Osobiście bardzo sobie cenię ten sposób, ponieważ pozwala odsiać w zasadzie wszystkie boty, o ile nie prowadzimy witryny pokroju yahoo.com ;-). Cała zabawa polega na ustawieniu w pliku z formularzem dodatkowego pola typu text z pustą wartością:

<input type="text" name="something_other" value="" />

Dodatkowo ukrywamy takie pole CSSem, stosując display: none;.

Jak wiemy, większość botów wypełnia automatycznie wszystkie pola formularza i natychmiastowo wysyła dane. Boty to komputery, dla niektórych – niestety, nie mają oczu i nie umawiają się na randki. Dlatego też bot nie zauważy, paradoksalnie, że dane pole jest ukryte przez CSS i potraktuje je jako zwykły element formularza, który trzeba wypełnić ofertą powiększenia tego i owego. Wystarczy więc sprawdzić, czy dane pole zostało wypełnione – jeśli tak, mamy do czynienia z botem bądź użytkownikiem przeglądarki tesktowej (bez CSS), który także zobaczy dodatkowe pole:

if (!empty($_POST['something_other'])) { exit(); die(); }

Od razu możemy wychwycić IP takiego delikwenta i na wszelki wypadek zapisać go do bazy, dzięki czemu, w większości będziemy mieć kompetentną listę botów, które należy zbanować.

Co więcej, tego typu puste pola możemy generować losowo, tak, by bot nie rozszyfrował nas, poznając stały atrybut name. Można więc losować name elementu text i zapisać go do sesji. Wtedy mamy o wiele więcej pewności, że bot nie pozna za szybko naszych intencji.

To tylko kilka przykładów, jak można zrobić detekcję botów. Na widnokręgu zagadnienia są: sprawdzanie włączonego JS (tutaj z kolei ryzykujemy użytkownikami z wyłączoną obsługą tego języka), odpowiednie chmody dla plików, autoryzacja HTTP, SSL, i wszelkiego rodzaju CAPTCHA (przy dodawaniu kontentu w miarę sprawne, choć do logowania bezużyteczne i wkurzające). Inna sprawa to także bezpieczna walidacja danych, temat ten jednak wypada omówić nieco szerzej, w bardziej rozległym kontekście. Z chęcią poznam też Wasze sposoby na tego typu ataki.

Komentarze

1

Z tym sprawdzaniem referera, to nie takie hop-siu ;) Z doświadczenia wiem, że spora część ambitniejszych użytkowników wyłącza w swoich przeglądarkach wysyłanie referara. I tym sposobem pozbawiasz ich możliwości wysłania formularza.

Ale za to jest jeszcze jeden skuteczny sposób. Zrobić dodatkowe pole w którym zapisujemy aktualny czas time(). Po wysłaniu formularza sprawdzamy, po jakim czasie został wysłany. Wiadomo – normalny człowiek potrzebuje na napisanie czegokolwiek przynajmniej te 5sec (takie bezpieczne minimum). Boty za to wysyłają formularze natychmiastowo, więc od razu zostaną odsiane.

2

Zapominasz o użytkownikach, którzy mają zapamiętane hasło w przeglądarce…

Albi
3

użycie time() nie jest dobrym pomysłem, ponieważ istnieje w przeglądarkach coś takiego jak zapamiętywanie haseł, formularz uzupełni się sam, a nam zostaje tylko kliknąć “zaloguj”. Moim zdaniem time() się sprawdzi, ale mimalny czas na uzupełnienie formularza nie mógł by przekraczać 1sec. (mi zalogowanie się na niektóre strony zajmuje 2-3sec)
A podoba mi się sposób z ukryciem formularza za pomocą css :)

psoders
4

@Albi uprzedziłeś mnie :)

psoders
5

No z tymi 5 sekundami to macie rację. Wyleciało mi po prostu z głowy, że chodzi konkretnie o formularz logowania. Bo w przypadku rejestracji, czy np komentowania to 5sec można dać spokojnie.

A jeżeli chodzi o sposób z ukrywaniem jednego inputa w CSS – potwierdzam 100% skuteczność takiego rozwiązania. Od ponad 2 lat nie nawiedził mnie ani jeden bot w komentarzach (a wcześniej była to prawdziwa plaga). Teraz problemu już nie ma w ogóle, bo formularz komentowania jest oparty tylko na AJAX, więc boty z natury omijają go szerokim łukiem ;)

6

A ja właśnie się zastanawiam, czy nie działałoby nazywanie formularzy jakoś dziwnie (tzn. w name jakieś losowe znaki), a potem walidacja konkretnych pól. Możnaby je nawet jakoś umieszczać w dziwnej kolejności, żeby zmylić bota. Na koniec walidacja pól i chyba powinno działać, co? W końcu wtedy bot nie wie co w którym polu wpisać, żeby komentarz przeszedł.

zx
7

Polecam użycie SBLAM SBLAM

8

Również korzystam z metody anty-captcha i sprawuje się wyśmienicie. Podejrzewam jednak, że w przypadku większych serwisów obejście to byłoby szybko zauważone. Mimo wszystko do tego momentu warto zaoszczędzić użyktownikom czasu :)

Najważniejsze jest to, by nie zakopać się w zabezpieczeniach, bo czasami chcąc zabezpieczyć się przed botami uniemożliwiamy dostęp do treści (patrz kotki na rapidshare…).

9

Co do wiekszych serwisow wlasnie, mysle ze mozna by tu polaczyc metode z-x’a, a mianowicie generowac w locie nazwy konkretnych pol i zapisywac je do sesji, tak by bot mial problem z rozroznieniem, co jest co. A zeby ukryc dane pole, moglibysmy pobierac jego klase czy tez id AJAXem przez JS i w JS wlasnie je ukrywac. No, ale tutaj dochodzi problem userow z JS. Ja chyba osobiscie wolalbym jakis alert o koniecznosci wlaczenia JS niz pseudo brzydka Captche.

Pozdro

10

Ja u siebie na stronie zastosowałem generowanie losowych sufixów nazw pól, które zapisuję sobie w sesji i jeżeli któregoś pola potem nie znajdę w wysłanych danych to banuję takiego osobnika. Poza tym sprawdzam liczę współczynnik zawartości tekstu do całości – wycinam z tekstu znaczniki <a>, [url] i [link] wraz z zawartością, i jeżeli jest mniej niż 50% tekstu, to formularz też idzie do kosza. Profilaktycznie też wcześniej wycinam spacje, żeby ktoś nie wysłał np. dziesięciu linków i 10k spacji :). Dodam jeszcze że na jednym testowym spamie współczynnik był 17% (ze spacjami 18,5%), więc próg 50% jest bezpieczny. Jak do tej pory zostałem zaspamowany tylko raz, jak bot wysyłał sam adres strony, bez znaczników.

11

Nie spodziewałem się że na stronie z której uczyłem się JS od podstaw znajdę to czego potrzebuje teraz bo takim okresie czasu (mianowicie skrypt przepisywania kodu z obrazka). Jednak na coś muszę zwrócić uwagę. Nie wiem czy ty dawałeś odnośnik do tego artykułu, ale zacytuje: “Sprawdzanie HTTP_REFERER jest kompletnie, ale to absolutnie totalnie bez sensu. Botowi banalnie prosto jest spreparować ten nagłówek, a z drugiej strony jest on kłopotliwy dla prawdziwych użytkowników, którzy mogą mieć go wycinanego lub modyfikowanego przez przeglądarkę, firewall albo proxy. Podobnie z Cookies, User-Agent i innymi danymi z HTTP.” Jest to artykuł z http://pornel.net/antyspam . Skorzystam z paru rozwiązań z tego skryptu :-) Pozdrawiam

12

Przepraszam za komentarz pod komentarzem, ale zastanawia mnie pewna rzecz. Mianowicie, jeżeli generuje nazwę pola które jest nie do wypełnienie, to jak ustawić styl danego pola zgodnie z W3C? Bo nie lubię, tak samo jak JS, wstawiania CSS bezpośrednio do kodu HTML. W grę nie wchodzi JS, bo chcę aby moja strona była zgodna z technologią Hijax. Pozdrawiam :)

13

@Misiur, to dołącz zewnętrzny CSS.

14

A ja zastanawiam się, czy dało by się jeszcze sprawdzić jakoś nazwę formularza w skrypcie logowania, tak aby odsiać tych, co ewentualnie próbują inteligentnie bokiem obejść zabezpieczenia?

uzi123
15

@Misiur: ukryj rodzica,
Poza tym nie trzeba zawsze używać dispaly:none. Choć kolejne rozwiązania nie sprawdzą się przy przeglądarkach używanych przez niewidomych
Może być:
overflow:hidden; width:1px; height:1px;
position:absolute; left:-1000px
vivsbility /*nigdy nie pamiętam jak to się pisze*/

@uzi123: wypełnianie pola przez javascript?

A wracając do wartości w formularzu i sesji (ticket), to bardzo polecam. Po użyciu, skasować wartość w sesji. Wtedy nie będzie problemu z danymi przesłanymi 2x.

Dodaj komentarz

Dozwolone tagi: <blockquote>, <code>, <strong>