Zabezpieczenia formularzy - metoda z hasłem na obrazku

piechnat

Mamy super skrypt i chcemy żeby wykonywany był tylko i wyłącznie przez formularz znajdujący się na naszej stronie.

***

Wstęp

Jeśli chcemy umożliwić urzytkownikowi wykonanie jakiejś akcji na naszej stronie, np wysłanie maila, smsa, stworzenie pliku, lub inną akcję, narażamy się na to, że złośliwi urzytkownicy zauomatyzują takie narzędzie, i nasza strona będzie wysyła dziesiątki tysięcy maili lub tworzyć tysiące plików. Mamy kilka sposobów żeby się przed tym zabezpieczyć, jedne bardziej skuteczne, lecz trudniejsze w implementacji, inne prostsze, ale za to mniej skuteczne.

Alternatywy

Na pewno najlepszym sposobem ochrony przed takimi atakami jest "captcha". Jest to zadanie przygotowane z myślą o użytkownikach, zadanie to powinno być banalne dla zwykłego użytkownika, a za razem bardzo trudne do zrobienia przez bota.

W starych czasach, takim zadaniem miało być rozpoznawanie tekstu. Aplikacja generowała obrazek na którym widniał tekst, często pisany różnymi kolorami i mającymi dodatkowe elementy, jak przekreślenia, podkreślenia, etc. Wszystko po to, by utrudnić botom korzystanie ze strony. Takie rozwiązania jednak stają się mniej użyteczne, z dwóch powodów.

  • Po pierwsze, ludzie stają się lewniwi, i czasem captcha odstraszała nawet ludzi, gdyż captcha była zbyt trudna. Programiści wtedy musieli tworzyć "generatory" nowych captchy, by użytkownik mógł sprawdzić kilka z nich i wpisać kolejną.
  • Po drugie, rozwój sztucznej inteligencji jest bardzo szybki, i wielu botom udało się już rozczytać captche z obrazków. Rozpoznawanie tekstu (z resztąnie tylko tekstu, ale również obrazków, twarzy, znaków drogowych etc.) już nie jest czysto ludzkom umiejętnością.

Najbardziej popularną i bezpieczną captchą obecnie stosowaną jest "ReCaptcha" firmy Google. Ta captcha polega na innych cecach ludzkiego użytkownika, a mianowicie: czas reakcji, przewijanie strony, ruchy myszką, błędy ludzkie.

Na pewno widziałeś captchę firmy Google nie raz.

captcha.png

Captcha firmy Google, również daje użytkownikowi proste zadanie - wystarczy kliknąć na checkbox. Zadanie proste do imitowania przez bota, prawda? Nie prawda.

Większość botów, kiedy automatyzuje swoją pracę zwykle przewija stronę na wyznaczoną wysokość (np window.scrollTo(0, 1200)), ustawia cursor nad elementem (np poprzez SetCursorPos(400, 200)), a potem wysyła komunikat o kliknięciu np poprzez WinApi lub API przeglądarki.

Jednak nie tak zachowuje się człowiek. Człowiek najpewniej będzie przewijał stronę, kręcąc kółkiem pare razy. Nie teleportuje też myszki idealnie nad checkbox, tylko przesunie myszkę powoli (jak to człowiek). Najpewniej nie przesunie też jej idealnie, tylko czasem wyjedzie poza kontur, i musi ją cofnąć. Dodatkowo, akcje te zajmują pewien czas (ludzki czas akcji wynosi około 50-250ms). Dodatkowo, dodatkowo, ludzki czas reakcji jest dosyć wolny, w porównaniu do komputera. Komputer, zaczyna wykonywać akcję, jak tylko dostanie takie polecenie. Człowiek nie zaczyna wykonywania swojej akcji dopóki nie zinterpretuje polecenie, a to zajmuje pewien czas.

Tak więc, jeśli captcha wykryje, że kliknięcie w checkbox było zbyt szybkie (zbyt szybki czas reakcji), zbyt dokładne (idealnie w punkt), kliknięcie zajęło mało czasu (mniej niż człowiek dałby radę to zrobić), i przesunięcie scrollbara było szybkie i dokładne, to jest sygnał że być może jednak to bot korzysta z naszej strony.

Komiplikacje

Ze względu na podniesienie poziomu bezpieczeństwa, recaptcha dostępna jest w dwóch wersjach. Tylko na frontendzie, lub zarówno na frontendzie i backendzie. Obie te funkcje są dosyć skomplikowane do implementacji.

Użytkowników zainteresowanych bezpieczeństwem odsyłam do dokumentacji Google: https://developers.google.com/recaptcha/intro

Rozwiązanie mniej-bezpieczne

Orginalny twórca tego artykułu zaproponował rozwiązanie mniej bezpieczne, polegające właśnie na ludzkiej umiejętności rozumienia tekstu.

Najlepiej jest przy pomocy formularza oprócz konkretnych danych przesłać hasło. Powinno ono za każdym razem być inne. Można to uzyskać na wiele sposobów. Najprościej jest generować hasło na podstawie aktualnego czasu. Sprawny 'hacker' formularzy jest w stanie bez problemu wyciągnąć hasło z kodu źródłowego strony dlatego właśnie pojawił się pomysł z wyświetleniem go w postaci obrazka co uniemożliwia programowe pobranie hasła. Jedyną wadą jest to, że użytkownik musi przepisywać je z obrazka do pola tekstowego ręcznie. Nasz super skrypt powinien przed wykonaniem swoich właściwych działań pobrać czas, rozkodować czas z hasła i sprawdzić czy oba czasy są sobie równe. W przypadku gdy tak się nie stanie powinien przerwać swoje działanie. Musimy oczywiście dać internaucie czas na wypełnienie formularza. Ja przyjąłem tolerancję do dwóch godzin.

Implementacja

Widok formularza - index.php:

<form action="skrypt.php" method="post">
  <!-- jakieś właściwe inputy -->
  <img src="image.php">
  <p>Aby wysłać formularz wpisz hasło z obrazka:</p>
  <input type="text" name="pass" size="8">
  <input type="submit" value="Wyślij">
</form>

Widok obrazka - image.php:

Autor korzysta tutaj z funkcji imageColorAllocate(), imageString(), imageJpeg() oraz imageDestroy(), do zwrócenia obrazka. Ustawia też odpowiedni MIME-TYPE w nagłówku HTTP.

<?php
function generatePassword() {
       $nr = floor(time() / 3600);
       srand(array_sum(explode(" ", microtime())) * 100000);
       $a = rand(0, min(getrandmax(), $nr)); 
       $b = $nr - $a;
       $a = base_convert($a, 10, 36); 
       $b = base_convert($b, 10, 36);
       $a = strrev(str_pad($a, max(strlen($a), strlen($b)), 0, STR_PAD_LEFT));
       $b = str_pad($b, max(strlen($a), strlen($b)), 0, STR_PAD_LEFT);
       for($i = 0; $i < strlen($a); $i++) $pass .= $a[$i].$b[$i];
       return $pass;
}
    header("Content-type: image/jpeg");
    $img = imageCreate(110, 30); 
    $background = imageColorAllocate($img, 206, 207, 099);
    $textcolor = imageColorAllocate($img, 0, 0, 0);
    imageString($img, 5, 10, 7, generatePassword(), $textcolor);  
    imageJpeg($img); 
    imageDestroy($img); 
?>

Nasz super skrypt - skrypt.php:

<?php
   function passwordCheck($pass) {
       $nr = floor(time() / 3600);
       for($i = 0; $i < strlen($pass); $i++)
         if($i % 2 == 0) $a .= $pass[$i]; else $b .= $pass[$i];
       $a = base_convert(strrev($a), 36, 10);
       $b = base_convert($b, 36, 10);
       if($a + $b <= $nr && $a + $b >= $nr - 2) return true; 
       return false;
   }
 
   if(!passwordCheck($pass)) die("Hasło nie poprawne !!!");

  // [...]

Uwagi

Należy jednak zaznaczyć, że funkcja generatePassword() oraz passwordCheck() mają wiele wad.

  • Polegają na dacie, więc każdy użytkownik aplikacji zobaczy to samo
  • Hasła mają zbyt dużą datę ważności, 2 godziny to jest zbyt dużo
  • Hasła są generowane metodą która nie jest kryptograficznie bezpieczna.

Jednak idea generowania obrazka, forularza, i walidowania go jest poprawna.

15 komentarzy

Dlaczego u mnie zawsze pokazuje że podano niepoprawne hasło
http://www.kruchy.eu/test/kod/index.php
skopiowałem dosłownie ten skrypt, poprawiłem znaki <> i nic. Proszę o info

Poprawiony znak specjalny html < na < , co czyniło z skryptu błędny

Ale przecież wszystkie take skrypty są bez sensu w porównaniu ze http://sblam.com !!!

Cóż... Mi ten skrypt nie działa..... mam je na http://www.elektrobed.yoyo.pl/zaasdbez/ . Prosze o kontakt. [email protected]

Nie wiem, czy zabezpieczenie obrazkiem jest takie pewne http://sam.zoy.org/pwntcha/.
Najlepsze wtedy wydaje się wyświetlanie grafiki w częściach, co raczej nie jest trudne do zrobienia

a nie lepiej (?) : wygenerowac haslo, zapisac w sesji i po wyslaniu formy sprawdzic czy sie zgadzaja

a nie lepiej (?) : wygenerowac haslo, zapisac w sesji i po wyslaniu formy sprawdzic czy sie zgadzaja

Takie rozwiązanie sprawia, że przy każdym kolejnym wygenerowaniu kodu działa jeszcze ten sprzed godziny więc praktycznie takie rozwiązanie mija się z celem zabezpieczenia.
Po za tym na większości już serwerów register globals jest wyłączone więc na początku skrypt.php powinno być:
$pass = $_POST['pass'];

dzięki za ten skrypt :)

Coś a'la bramka na stronie idei możecie znaleźć tutaj: http://ztmnews.kernel.pl/token/ -- bardzo proste do wykonania to było, ale wykorzystuje mysql'a

np na stronie idola przy autoryzacji kody dostepu, bramka tesh, ale cos sie im wali, bo niepoprawnie odczytuje wpisany text

Heh... Bramka SMS Idei ...

Ciekawy skrypt, często się z takimi spotykam, a w szczególności np dla autoryzacji kodów otrzymanych sms-em na wielu portalach