Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Klasa] Antibot - przyjazna ochrona przed botami
Forum PHP.pl > Inne > Oceny
phpion
Witam,
dziś napisałem prosty mechanizm ochrony przed spamującymi botami.

Demo modułu można zobaczyć tutaj:
http://kohana.phpworkstation.com/antibottest

Paczuszkę z niezbędnymi plikami oraz przykładem wykorzystania można pobrać stąd:
http://kohana.phpworkstation.com/download/antibot.zip

Oto pliki wchodzące w skład modułu:

modules/antibot/config/antibot.php
  1. <?php
  2. $config = array(
  3.    /**
  4.      * Tajny klucz służący do zakodowania nazw plików graficznych.
  5.      */
  6.    'security_key' => 'mójmegatajnyklucz',
  7.  
  8.    /**
  9.      * Nazwa katalogu / ścieżka katalogu z plikami graficznymi.
  10.      */
  11.    'images_directory' => 'img/antibot',
  12.    'randoms' => array(
  13.        /**
  14.          * Liczba losowanych poprawnych elementów.
  15.          */
  16.        'good' => 2,
  17.  
  18.        /**
  19.          * Liczba losowanych niepoprawnych elementów.
  20.          */
  21.        'bad' => 3
  22.    ),
  23.    'images' => array(
  24.        /**
  25.          * Tablica nazw poprawnych plików graficznych.
  26.          */
  27.        'good' => array(
  28.            '1.jpg',
  29.            '2.jpg',
  30.            '3.jpg',
  31.            '4.jpg',
  32.            '5.jpg'
  33.        ),
  34.  
  35.        /**
  36.          * Tablica nazw niepoprawnych plików graficznych.
  37.          */
  38.        'bad' => array(
  39.            '10.jpg',
  40.            '20.jpg',
  41.            '30.jpg',
  42.            '40.jpg',
  43.            '50.jpg'
  44.        ),
  45.    )
  46. );
  47. ?>


modules/antibot/libraries/Antibot.php
  1. <?php
  2. /**
  3.  * Klasa tworząca przyjazne zabezpieczenie formularzy przed atakami botów.
  4.  *
  5.  * Działanie klasy polega na wyświetleniu określonej liczby plików graficznych,
  6.  * wśród których znajduje się pewna liczba elementów, które należy zaznaczyć w formularzu.
  7.  *
  8.  * @author phpion
  9.  */
  10. class Antibot {
  11.    /**
  12.      * Losowy ciąg znaków wykorzystywany przy kodowaniu nazwy pliku graficznego.
  13.      *
  14.      * @var string
  15.      */
  16.    private $token = '';
  17.  
  18.    /**
  19.      * Tajny klucz bezpieczeństwa wykorzystywany przy kodowaniu nazwy pliku graficznego.
  20.      *
  21.      * @var string
  22.      */
  23.    private $security_key = '';
  24.  
  25.    /**
  26.      * Nazwa katalogu / ścieżka katalogu, w którym znajdują się pliki graficzne.
  27.      *
  28.      * @var string
  29.      */
  30.    private $images_directory = '';
  31.  
  32.    /**
  33.      * Tablica zawierająca informację o ilości losowanych plików graficznych z rozróżnieniem na typ (dobry/zły).
  34.      *
  35.      * $array = array(
  36.      *        'good' => 2,
  37.      *        'bad'  => 3
  38.      * );
  39.      *
  40.      * @var array
  41.      */
  42.    private $randoms = array();
  43.  
  44.    /**
  45.      * Tablica nazw plików graficznych służących do wyświetlania dobrych/złych elementów.
  46.      *
  47.      * $array = array(
  48.      *        'good' => array(1, 2, 3),
  49.      *        'bad'  => array(10, 20, 30
  50.      * );
  51.      *
  52.      * @var <type> tablica
  53.      */
  54.    private $images = array();
  55.  
  56.    /**
  57.      * Konstruktor klasy Antibot.
  58.      *
  59.      * Automatycznie pobiera potrzebne dane z pliku konfiguracyjnego.
  60.      */
  61.    public function __construct() {
  62.        $config = Kohana::config('antibot');
  63.  
  64.        $this->security_key = (string)$config['security_key'];
  65.        $this->images_directory = (string)$config['images_directory'];
  66.        $this->randoms = (array)$config['randoms'];
  67.        $this->images = (array)$config['images'];
  68.  
  69.        $this->set_token();
  70.    }
  71.  
  72.    /**
  73.      * Losuje listę plików graficznych.
  74.      *
  75.      * @return array Tablica zakodowanych nazw plików graficznych.
  76.      */
  77.    public function get_images() {
  78.        $return = array();
  79.  
  80.        foreach (array(self::GOOD, self::BAD) as $type) {
  81.            shuffle($this->images[$type]);
  82.            $images = array_chunk($this->images[$type], (int)$this->randoms[$type]);
  83.  
  84.            $return = array_merge($return, $images[0]);
  85.        }
  86.  
  87.        shuffle($return);
  88.  
  89.        foreach ($return as $key => $item) {
  90.            $return[$key] = $this->encode_image($item);
  91.        }
  92.  
  93.        return $return;
  94.    }
  95.  
  96.    /**
  97.      * Koduje nazwę pliku graficznego.
  98.      *
  99.      * @param string $image Nazwa pliku graficznego.
  100.      * @return array Zakodowana nazwa pliku graficznego.
  101.      */
  102.    private function encode_image($image) {
  103.        $input = Input::instance();
  104.  
  105.        return md5($this->encode_token().$image.$input->ip_address().$this->security_key);
  106.    }
  107.  
  108.    /**
  109.      * Wyszukuje prawdziwą nazwę pliku graficznego na podstawie jego zakodowanej nazwy.
  110.      *
  111.      * @param string Zakodowana nazwa pliku graficznego.
  112.      * @return string|boolean Prawdziwa nazwa pliku graficznego jeśli zostanie znaleziony; w przeciwnym razie zwraca FALSE.
  113.      */
  114.    private function find_image($encoded_image) {
  115.        $images = array_merge($this->images[self::GOOD], $this->images[self::BAD]);
  116.  
  117.        foreach ($images as $image) {
  118.            if ($this->encode_image($image) === $encoded_image) {
  119.                return $image;
  120.            }
  121.        }
  122.  
  123.        return FALSE;
  124.    }
  125.  
  126.    /**
  127.      * Zwraca obiekt typu Image reprezentujący plik graficzny.
  128.      *
  129.      * @param string Zakodowana nazwa pliku graficznego.
  130.      * @return Image|boolean Obiekt typu Image jeśli znaleziono prawdziwy plik graficzny; w przeciwnym razie zwraca FALSE.
  131.      */
  132.    public function get_image($encoded_image) {
  133.        $decoded_image = $this->find_image($encoded_image);
  134.  
  135.        return $decoded_image ? new Image($_SERVER['DOCUMENT_ROOT'].Kohana::config('config.site_domain').$this->images_directory.'/'.$decoded_image) : FALSE;
  136.    }
  137.  
  138.    /**
  139.      * Koduje token.
  140.      *
  141.      * @return string Zakodowany token.
  142.      */
  143.    private function encode_token() {
  144.        $return = $this->token;
  145.        $return = strrev($return);
  146.        $return = md5($return);
  147.        $return = substr($return, 9, 14);
  148.        $return = md5($return);
  149.  
  150.        return $return;
  151.    }
  152.  
  153.    /**
  154.      * Sprawdza czy użytkownik wskazał poprawne elementy z przedstawionej mu listy.
  155.      *
  156.      * @param array $checked Tablica zaznaczonych elementów.
  157.      * @return boolean TRUE jeśli zaznaczono poprawne elementy; w przeciwnym razie zwraca FALSE.
  158.      */
  159.    public function validate(array $checked = array()) {
  160.        $checked = array_unique($checked);
  161.  
  162.        if (count($checked) === (int)$this->randoms[self::GOOD]) {
  163.            $correct = 0;
  164.  
  165.            foreach ($this->images[self::GOOD] as $item) {
  166.                if (in_array($this->encode_image($item), $checked)) {
  167.                    $correct++;
  168.                }
  169.            }
  170.  
  171.            return $correct === (int)$this->randoms[self::GOOD];
  172.        }
  173.  
  174.        return FALSE;
  175.    }
  176.  
  177.    /**
  178.      * Ustawia token dla instancji klasy.
  179.      *
  180.      * Jeżeli nie przekazano parametru $token wówczas nadawany jest losowy token.
  181.      *
  182.      * @param string|NULL $token Token.
  183.      */
  184.    public function set_token($token = NULL) {
  185.        if (is_null($token)) {
  186.            $token = md5(uniqid());
  187.        }
  188.  
  189.        $this->token = (string)$token;
  190.    }
  191.  
  192.    /**
  193.      * Zwraca token.
  194.      *
  195.      * @return string Token.
  196.      */
  197.    public function get_token() {
  198.        return $this->token;
  199.    }
  200.  
  201.    /**
  202.      * Nazwa klucza dla poprawnych elementów.
  203.      */
  204.    const GOOD = 'good';
  205.  
  206.    /**
  207.      * Nazwa klucza dla niepoprawnych elementów.
  208.      */
  209.    const BAD = 'bad';
  210. }
  211. ?>


modules/antibot/controllers/antibot.php
  1. <?php
  2. /**
  3.  * Kontroler odpowiedzialny za wyświetlanie odpowiedniego pliku graficznego.
  4.  */
  5. final class Antibot_Controller extends Controller {
  6.    /**
  7.      * Zwraca do przeglądarki konkretny plik graficzny.
  8.      */
  9.    public function render_image() {
  10.        $image = (string)$this->input->get('image', '');
  11.        $token = (string)$this->input->get('token', '');
  12.  
  13.        $ab = new Antibot();
  14.        $ab->set_token($token);
  15.  
  16.        $image = $ab->get_image($image);
  17.  
  18.        if ($image instanceof Image) {
  19.            echo $image->render();
  20.        }
  21.    }
  22. }
  23. ?>

Jeżeli macie jakiekolwiek uwagi czy sugestie to chętnie się z nimi zapoznam smile.gif

Pozdrawiam,
pion
wookieb
A czy dobrze napisany graficzny token nie bedzie lepszy?
System wybierania obrazków jest troche duzy a mniejsze wersja moze byc nieuzyteczna (Chodzi mi o zajetosc na stronie)
Uwazam ze najlepszym systemem antybotowym jest odpowiadanie na proste pytania. Np jest tak w wyszukiwarce na flashzone.pl
Pytanie typu: Jakiego koloru jest śnieg.
Przy odpowiednio częstym zmienianiu pytań, żaden bot tego nie łyknie.
Riklaunim
Ja tam stosuję tekstowe zabezpieczenie - podanie n-tej cyfry z podanej liczby. Automatyczne boty się na tym wysypują winksmiley.jpg
pyro
Cytat(Riklaunim @ 14.05.2009, 22:45:14 ) *
Ja tam stosuję tekstowe zabezpieczenie - podanie n-tej cyfry z podanej liczby. Automatyczne boty się na tym wysypują winksmiley.jpg


no zalezy jak to przedstawiasz uzytkownikowi... ja tam korzystam z captchy (klasa od google) i zabezpieczenie 100% skuteczne. @phpion, mi niestety pomysł nie wydaje się zbytnio użyteczny z powodów, które podał @wookieb. To troche proba wymyslenia czegos, co jest niepotrzebne winksmiley.jpg
Crozin
Poza tym, nadal istnieje spora szansa na to, że bot złamie to - na zasadzie zaznaczania losowych checkboxów.
phpion
Cytat(wookieb @ 14.05.2009, 22:39:44 ) *
A czy dobrze napisany graficzny token nie bedzie lepszy?

Jak dla mnie captcha nie jest cudem wygody dla użytkownika końcowego stąd pomysł (w sumie gdzieś to już widziałem) o innym podejściu do zabezpieczenia.

Cytat(pyro @ 14.05.2009, 22:50:02 ) *
ja tam korzystam z captchy (klasa od google) i zabezpieczenie 100% skuteczne

j.w.

Cytat(Crozin @ 15.05.2009, 00:08:03 ) *
Poza tym, nadal istnieje spora szansa na to, że bot złamie to - na zasadzie zaznaczania losowych checkboxów.

Przy większej liczbie prawidłowych obrazków (np. 5 dobrych + 5 złych) szanse te są mniejsze. Poza tym wystarczy co jakiś czas zmienić ten stosunek i już smile.gif

Tak czy siak dzięki za opinie.
Crozin
Cytat
Przy większej liczbie prawidłowych obrazków (np. 5 dobrych + 5 złych) szanse te są mniejsze. Poza tym wystarczy co jakiś czas zmienić ten stosunek i już
Jest 6:20 i musze lecieć na autobus więc liczyć nie będę, ale wystarczy, że jedna na 100 prób "na ślepo" się powiedzie i już trzeba się liczyć z dziesiątkami botów dziennie, które przejdą walidację.

A czytanie instrukcji + rozpoznawanie (nie)prawidłowych obrazków przez użytkownika wydaje mi się prawdę powiedziawszy nieco trudniejsze niż sama CAPTCHA.
nieraczek
Captcha jest beznadziejna, zwykle tam gdzie to jest to rezygnuje z rejestracji, chyba że bardzo mi zależy na rejestracji na danej stronie. Z jakich powodów ? Zz takich powodów, że zwykle muszę wpisywać kilka razy kod z obrazka bo zawsze się mylę, np. nie jestem w stanie odróżnić litery O od cyfry 0, litery I od cyfry 1 itd. Może to i chroni przed spambotami, ale ja zwykle rezygnuję z rejestracji w takich serwisach, bo nie jestem w stanie rozczytać co tam jest nabazgrane.
pyro
Cytat(nieraczek @ 15.05.2009, 09:00:06 ) *
Captcha jest beznadziejna, zwykle tam gdzie to jest to rezygnuje z rejestracji, chyba że bardzo mi zależy na rejestracji na danej stronie. Z jakich powodów ? Zz takich powodów, że zwykle muszę wpisywać kilka razy kod z obrazka bo zawsze się mylę, np. nie jestem w stanie odróżnić litery O od cyfry 0, litery I od cyfry 1 itd. Może to i chroni przed spambotami, ale ja zwykle rezygnuję z rejestracji w takich serwisach, bo nie jestem w stanie rozczytać co tam jest nabazgrane.


To sobie sciagnij captche od google, sa tam uzywane słowa a nie losowe litery
pijanyadmin
Nie ma captche od google, jest tylko cpatcha podobna do tej z google.
pyro
Cytat(pijanyadmin @ 15.05.2009, 13:23:17 ) *
Nie ma captche od google, jest tylko cpatcha podobna do tej z google.


Miałem to na myśli winksmiley.jpg

link: http://code.google.com/p/cool-php-captcha
pijanyadmin
Poza tym, nieraczek to Ty nie masz konta praktycznie nigdzie? biggrin.gif Nawet emaila wp,gmail? To straszne... Poza tym nie widze problemu aby pisząc takie "bajery" wykluczyć znaki "o01l" itp. Przy stronie o cioci Krysi nie ma sensu pisać captcha, ale przy większych serwisach jest to wskazane.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.