Zabezpieczenie formularzy – captcha

Jak wiadomo, w internecie występują boty – programy których celem jest np. poszukiwanie i wysyłanie formularzy z przypadkową treścią (spamem). Gdy taki bot znajdzie formularz kontaktowy to może wysyłać setki maili ze spamem. Należałoby więc tak zabezpieczyć formularz, aby utrudnić przejście validacji przez bota. Człowiek powinien poradzić sobie bez większych problemów. Mowa tutaj o przepisywaniu liter z obrazka – captcha. Przy okazji przestrzegam przed umieszczaniem mail w formie jawnej lub odnośnikach

<a href="mailto:mail@example.com">kontakt</a>

Klasa captcha przy tworzeniu obrazków korzysta z modułu php GD2. Na większości serwerów jest dostępny.
Klasę można pobrać captcha (650)
W archiwum znajdują się także czcionki, z których captcha losuje przy generowaniu znaków.

Jak ją zaimplementować? Zaprezentuję wdrożenie captcha do Drugie starcie (formularza kontaktowego)
1. Pobieramy archiwum i rozpakowujemy do naszej aplikacji.
2. Modyfikujemy kontroler w którym jest walidacja (kontroler static, akcja contact)

/application/classes/controller/static.php »

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Static extends Controller_Default {

    public function action_about() {
        $this->template->content=View::factory('aboutus')
            ->bind('title',$this->template->title);
        $this->template->title=__('About us');
        $this->template->top_tab='about';
    }

    public function action_contact() {
        $this->template->content=View::factory('contact')
            ->bind('msg',$msg)
            ->bind('errors',$errors)
            ->bind('data',$data)
            ->bind('title',$this->template->title)
            ->bind('captcha',$captcha);
        $this->template->title=__('Contact');
        $this->template->top_tab='contact';
        $driver=''; switch(rand(0, 5)){
                    case '0': $driver='alpha';break;
                    case '1': $driver='black';break;
                    case '2': $driver='alpha';break;
                    case '3': $driver='black';break;
                    case '4': $driver='';break;}
        $captcha = new Captcha($driver);
        
        if(isset($_POST['submit'])){
            $post=Validation::factory($_POST)
                ->rules('email', array(array('not_empty'),array('email')))
                ->rules('username', array(array('not_empty'),array('alpha_dash'),array('min_length', array(':value', 3)),array('max_length', array(':value', 20))))
                ->rule('content', 'not_empty')
                ->rules('captcha',Kohana::$config->load('captcha')->rules)
                ->labels(array('email'=>'E-mail','username'=>'Nick','content'=>'Content','captcha'=>'Characters'));
            if($post->check()){
                //send email
                $msg='<p class="green">'.__('Message was sent').'</p>';
                unset($_POST);
            }else{
                $data=$_POST;
                $errors=$post->errors('messages');
            }
        }
    }
}?>

Co zostało zmienione?
Przesyłamy do widoku zmienną przez referencję, pod którą będzie obrazek

->bind('captcha',$captcha)

Losujemy sterownik i tworzymy obrazek o różnych właściwościach z configu (driver zawiera właściwości takie jak kolor tła, ilość liter)

$driver=''; switch(rand(0, 5)){
    case '0': $driver='alpha';break;
    case '1': $driver='black';break;
    case '2': $driver='alpha';break;
    case '3': $driver='black';break;
    case '4': $driver='';break;}
$captcha = new Captcha($driver);

Pobieramy przy walidacji reguły dla captcha z configu (nie może być pusty, musi się zgadzać z obrazkiem) i zmieniamy captcha w błędach validacji na Characters

->rules('captcha',Kohana::$config->load('captcha')->rules)
->labels(array('email'=>'E-mail','username'=>'Nick','content'=>'Content','captcha'=>'Characters'));

Jak nie podamy prawidłowych liter Validacja nie przejdzie i zostanie wyświetlony komunikat błędu.

3. Aktualizujemy widok odpowiedzialny za wyświetlenie formularza kontaktowego

/application/views/contact.php »

<h2><?php echo $title?></h2>
<?php echo  Form::open('/contact', array('class'=>'form'))?>
<fieldset>
<legend><?php echo __('Fill the fields')?></legend>
    <?php echo $msg?>
    <?php echo Form::label('email', __('E-mail').': ').Form::input('email', Arr::get($data, 'email'))?> <span class="red"><?php echo  Arr::get($errors, 'email')?></span><br />
    <?php echo Form::label('username', __('Nick').': ').Form::input('username', Arr::get($data, 'username'))?> <span class="red"><?php echo Arr::get($errors, 'username')?></span><br />

    <?php echo Form::label('contetn', __('Content').': ',array('class'=>'fll')).Form::textarea('content', Arr::get($data, 'content'), array('cols'=>'40'))?> <span class="red"><?php echo  Arr::get($errors, 'content')?></span><br />
</fieldset>
<fieldset>
    <legend><?php echo __('Security')?></legend>
    <?php echo Form::label('image', __('Image').': ', array('style'=>'float:left')).$captcha;?><br/>
    <?php echo Form::label('captcha', __('Characters').': ').Form::input('captcha', Arr::get($data, 'captcha'))?> <span class="red"><?php echo Arr::get($errors, 'captcha');?></span><br />
</fieldset>
<?php echo Form::submit('submit', __('Send')).Form::close();?><br />

Wyświetlamy obrazek i input do wpisywania znaków

<fieldset>
    <legend><?php echo __('Security')?></legend>
    <?php echo Form::label('image', __('Image').': ', array('style'=>'float:left')).$captcha;?><br/>
    <?php echo Form::label('captcha', __('Characters').': ').Form::input('captcha', Arr::get($data, 'captcha'))?> <span class="red"><?php echo Arr::get($errors, 'captcha');?></span><br />
</fieldset>

4. Dodajemy tłumaczenie na pl w pliku /application/i18n/pl.php

'Incorrect code from the image'=> 'Nieprawidłowy kod z obrazka',

To by było wszystko. Oczywiście można poeksperymentować zmieniając lub dodając drivery w configu. W samą klasę nie musimy się zagłębiać…

Klasa dla KO 3.3: Captcha Kohana 3.3.x (257)

18 Odpowiedzi :“Zabezpieczenie formularzy – captcha”

  1. tojach napisał:

    Czy to zadziała w Kohanie 3.2?

  2. Mariusz napisał:

    Niby ze starszej wersji, ale poprawiłem odwołania do configu (powinno to wystarczyć) i działa na KO3.2 (implementowane do Drugie starcie).

  3. Łukasz napisał:

    ej witaj.
    nie wiedziałbyś może czemu mimo wszystko wywala mi błąd, ze captcha nie może być pusta, nawet jak wpisze coś do pola characters

  4. Adam napisał:

    Witam, a jak zastosować tą walidację skoro skorzystam z tej z ORM? próbowałem w ten sposób, ale nie działa…

    	public function rules()
    	{
    		return array(
    			'username' => array(
    				array('not_empty'),
    				array('max_length', array(':value', 32)),
    				array(array($this, 'unique'), array('username', ':value')),
    				array('alpha_numeric'),
    			),
    			'password' => array(
    				array('not_empty'),
    			),
    			'email' => array(
    				array('not_empty'),
    				array('email'),
    				array(array($this, 'unique'), array('email', ':value')),
    			),
    			'captcha' => array(
    				array(Kohana::$config->load('captcha')->rules)
    			),
    		);
    	}
    • Adam napisał:

      ok juz sobie poradziłem, wystarczyło użyć dodatkowej walidacji tak samo jak to sie robi z password_confirm :)

  5. Adam napisał:

    Mam problem, mianowicie jak utworzę drugi nowy obiekt klasy Captcha, to pierwszy przejmuje wartość drugiego. Wygląda dokładnie tak samo.

    Jak zrobić, aby mieć dwa różne Captcha na jednej stronie?

    • Mariusz napisał:

      Nadajesz inną nazwę i odwołujesz się do niej przy drugim captcha?

      $captcha2 = new Captcha($driver);
      

      I w widoku

          <?php echo Form::label('image', __('Image').': ', array('style'=>'float:left')).$captcha2;?><br/>
      
      • Adam napisał:

        tak, mimo wszystko wplywa to na tą pierwszą choć jest w innej zmiennej. nie wiem dlaczego tak sie dzieje…

      • Mariusz napisał:

        Po konsultacji z kolegą muszę powiedzieć, że niestety to dodaje zmienną captcha_response do sesji i jak zrobisz drugie captcha to zastąpisz w sesji to pierwsze. Trzeba by to trochę zmodernizować.

  6. mathies napisał:

    Bardzo przydatna rzecz. Czy zadziała bez przeróbek na kohana 3.3.1?
    Niestety, gdy dodam linijkę:

    $captcha = new Captcha($driver);

    serwer się wiesza na podstronie (Error 500).
    Dopiero zaczynam przygodę z Kohaną, ale wydaje mi się, że zrobiłem wszystko zgodnie z punktem 1.
    W punkcie 2 mam problem, bo nie mogę znaleźć (kohana też) klasę Controller_Default, a z Controller_Template jest właśnie wspomniany Error 500.
    Z góry dziękuję za pomoc

  7. mathieus napisał:

    poprawka: znalazłem controler_default w „Drugie starcie”, ale i tak – nie działa :(

    • Mariusz napisał:

      Trzeba by dostosować moduł: Migrating from 3.2.x, albo poszukać modułu dla ko 3.3.

      Nie wiem też na jakim jesteś etapie, masz już coś czy dopiero zaczynasz? Jeśli zaczynasz to przeanalizuj Hello, World a następnie Drugie starcie – zebrana wiedza [KO3.2]. Zapoznasz się jak są ładowane kontrolery czy widoki, potem będzie łatwiej odpalić jakiś moduł. Dodatkowo w ko 3.3 były pewne zmiany (pierwszy link), które to wymagają np. nazywania niektórych plików z dużych liter.

  8. mathieus napisał:

    Faktycznie – duże litery wiele zmieniają – thx.
    Popełniłem też spore gapiostwo – zostawiłem w bootstrapie:

     Kohana::init(array(
    (...)	'errors' => FALSE,
    ));
    

    przez co nie widziałem, szczegółów tego co się dzieje.
    Obecnie mam błąd:

    ErrorException [ Warning ]: Invalid argument supplied for foreach()
    APPPATH/classes/Captcha.php [ 89 ]
     // Assign config values to the object
    89         foreach ($config as $key => $value)
    90         {
    91             if (isset(Captcha::$config[$key]))
    92             {
    93                 Captcha::$config[$key] = $value;
    94             }
    

    Dzięki za artykuł, a szczególnie za bierzące wspieranie koderów – brak kompatybilności wstecznej jest sporą komplikacją.

  9. mathieus napisał:

    Działa pod 3.3.1…
    zmiany które trzeba wprowadzić:
    - zmiana pierwszej litery na dużą:
    – całej masy (ale nie wszystkich) plików
    – wywołań klasy text -> Text
    – wywolań klasy url -> URL
    oraz dodanie w bootstrapie (żeby się wyświetliły obrazki):

    Route::set('static', '(captcha/(<action>))') 
    	->defaults(array( 
        		'controller' => 'captcha', 
    		'action' => 'default', 
    	));
    

Dodaj komentarz

Dodając kod PHP używaj tagów: [php][/php]

*