Материалы
[:NetFAQ://]


Поиск

[:NetFAQ://]


Партнеры
Хостинг от Park-Web

www.popularsite.ru
[:NetFAQ://]


Реклама
[:NetFAQ://]


Мат. часть. Алгоритм борьбы с роботами (captcha)

Мат. часть. Алгоритм борьбы с роботами (captcha)


Про....ь с десятком готовых алгоритмов проверки. Решено было придумать свой. Голова вроде соображает...

Требования:

1. И основное. Страница, с формой и страница со скриптом, который обрабатывает данные с формы, одна и таже.
2. Не использовать сессии. Во впервых их и так хватает, во вторых глюки при работе с сессиями из-за ЧПУ + п.1 + сервер
3. Генерация картинок, должна по возможности использовать набор шрифтов.
4. Картинки не должны сохранятся на сервере.

Схема проверки

После недолгих размышлений, была придумана схема, для проверки правильности введенного кода.

Как будем проверять.

Принцип простой. Есть пара ключ1+ключ2 и волшебная функция md5 , которая будет независимым хранителем ключей.
Ключ1 - набор символов изображенный на картинке.
Ключ2 - какая либо случайная величина например: результат работы функции time()
Ключ3 - md5(Ключ1+Ключ2) - строка, которая будет хранится в базе данных и с ней сравниваются данные из формы.

При генерации картинки, Ключ3 будет заносится в базу данных, для сохранения. Ключ2 предается в явном виде через форму, Ключ1 вводится пользователем, на основе картинки.

Скрипт, обрабатывающий форму, генерирует ключ3 и сравнивает его с ключами в базе данных, если ключ существует, то оставшаяся часть формы обрабатывается.

Реализация

Так как программисты люди ленивые, то генератор картинок, мы скачаем из интернета. Не буду описывать всего процесса поиска, не интересно. Нужный класс был найден довольно быстро>

Class: captchaSteroid This class can be used to generate CAPTCHA validation images with distorted text.

It can render the validation text in an image. After the text is rendered, a new image is generated applying wave distortion effects to the original image, in order to make the text harder to guess by robots.

The image size, text font, background color, and background image are configurable parameters.


Его и будем дорабатывать напильником.
Сперва. Создадим таблицу в базе данных, которая будет хранить ключ и временную метку, которая нужна для удаления старых не использованных ключей.

1
2
3
4
CREATE TABLE IF NOT EXISTS `captcha` (
  `key` varchar(255) collate utf8_unicode_ci NOT NULL,
  `timestamp` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


Далее, разбираем то что скачали. Ниже приведен, листинг класса для генерации изображений.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
<?php class captchaSteroid
{
    
    function captchaSteroid($config = array())
    {
        $this->config = $config;
        
        if(!isset($this->config['width']))
            $this->config['width'] = 200;
 
        if(!isset($this->config['height']))
            $this->config['height'] = 70;
 
        if(!isset($this->config['font']))
            $this->config['font'] = null;
 
        if(!isset($this->config['transparency']))
            $this->config['transparency'] = '000000';
 
        if(!isset($this->config['background_color']))
            $this->config['background_color'] = 'ffffff';
 
        if(!isset($this->config['background_image']))
            $this->config['background_image'] = null;
 
        if(!isset($this->config['color']))
            $this->config['color'] = '0000aa';
 
    }
    
    function drop($text=null)
    {
        
        if(!$this->config['width'] || !is_int($this->config['width']))
            trigger_error("\$config['width'] not int > 0", E_USER_ERROR);
 
        if(!$this->config['height'] || !is_int($this->config['height']))
            trigger_error("\$config['height'] not int > 0", E_USER_ERROR);
 
        if(!$this->config['font'])
            trigger_error("\$config['font'] not defined", E_USER_ERROR);
        
        if(!$text)
        {
            $text = substr(md5(microtime()),0,6);
        }
        
        $font_size = (int) $this->config['height'] * 1.2;
        
        $height = $this->config['height'];
        $width = $this->config['width'];
        
        $image_buffer = imagecreatetruecolor($width, $height);
        $image_final = imagecreatetruecolor($width, $height);
 
        $background_color = captchaSteroid::_color($this->config['background_color'], $image_buffer);
 
        $color = captchaSteroid::_color($this->config['color'], $image_buffer);
 
        $transparent = captchaSteroid::_color($this->config['transparency'], $image_buffer);
        
        imagefilledrectangle( $image_final, 0, 0,$width-1,$height-1, $background_color );
        
        if($this->config['background_image'])
        {
 
            if(!file_exists($this->config['background_image']))
                trigger_error("Invalid \$config['background_image']: ".$config['background_image'], E_USER_ERROR);
 
            $image_bg = imagecreatefromjpeg($this->config['background_image']);
            imagecopy($image_final, $image_bg, 0, 0, 0, 0, $width, $height);
            imagedestroy($image_bg);
            
        }
        
        $angle = rand(-10, 10);
        
        $temp_info = imageftbbox ($font_size, $angle, $this->config['font'], $text);
        
        $temp_width1 = abs($temp_info[0]) + abs($temp_info[4]);
        $temp_height1 = abs($temp_info[1]) + abs($temp_info[5]);
        
        $temp_width2 = abs($temp_info[6]) + abs($temp_info[2]);
        $temp_height2 = abs($temp_info[3]) + abs($temp_info[7]);
        
        $temp_width  = 8 + ($temp_width1  > $temp_width2  ? $temp_width1  : $temp_width2  ); 
        $temp_height = 4 + ($temp_height1 > $temp_height2 ? $temp_height1 : $temp_height2 );
        
        $xcoord = $temp_info[0] > $temp_info[6] ? $temp_info[0] : $temp_info[6]; 
        $ycoord = $temp_info[7] > $temp_info[5] ? -$temp_info[5] : -$temp_info[7];
        
        $image_temp = imagecreatetruecolor($temp_width, $temp_height);
        
        
        $red = imageColorAllocate($image_buffer, 255, 50, 50);
        imageColorTransparent($image_buffer, $transparent);
        imageColorTransparent($image_temp, $transparent);
        imageColorTransparent($image_final, $transparent);
        
        
        imagefilledrectangle( $image_temp, 0, 0,$temp_width-1,$temp_height-1, $transparent );
        imagefilledrectangle( $image_buffer, 0, 0,$temp_width-1,$temp_height-1, $transparent );
        
    
        imagefttext($image_temp, $font_size, $angle, $xcoord +4, $ycoord +2, $color , $this->config['font'], $text );
        
        imagecopyresized($image_buffer, $image_temp, 0, 0, 0, 0, $width, $height, $temp_width, $temp_height);
        
        imagedestroy($image_temp);
 
        
        captchaSteroid::distortion($image_buffer, $transparent, $width, $height);
    
        imagecopymerge( $image_final, $image_buffer, 0, 0, 0, 0, $width, $height, 100 );
 
        header("Content-type: image/jpeg");
        imagejpeg($image_final);
        //imagepng($image_final);
        
        imagedestroy($image_final);
        imagedestroy($image_buffer);
        
    }
 
 
    function distortion($image,$background,$width=0,$height=0)
    {
        if(!$width) $width = imagesx($image);
        if(!$height) $height = imagesy($image);
 
        $background = captchaSteroid::_color($background, $image);
 
        $orig = imagecreatetruecolor($width,$height );
        imageColorTransparent($orig, $background);
        
        imagecopy( $orig, $image, 0, 0, 0, 0, $width, $height );
        
        imagefilledrectangle( $image, 0, 0,$width,$height, $background );
 
        
        $v_f1 = rand ( 250, 300 )/100; //  od 2 do 3 za fju
        $v_w1 = rand ( 0, $width * 100 )/100;// od 0 do width
    
        $v_f2 = rand ( 40, 60 )/100;// od 0.1 do 1 za prigusenje
        $v_w2 = rand ( 0, $width * 100 )/100; // od 0 do width
    
        $v_f3 = rand ( 40, 60 )/100;// od 0.1 do 1 za prigusenje dolje
        $v_w3 = rand ( 0, $width * 100 )/100; // od 0 do width
        
        $y4_max = 0;
        $y5_max = 0;
        
        $an = array();
        $as = array();
    
        for($x=0; $x<$width; $x++)
        {
            
            $y1 = captchaSteroid::_sin($x, $v_f1, $v_w1, $width, $height);
            $y2 = captchaSteroid::_sin($x, $v_f2, $v_w2, $width, $height);
            $y3 = captchaSteroid::_sin($x, $v_f3, $v_w3, $width, $height);
            
            $y4 = $y1 * $y2 / $height / 3;
            $y5 = $y1 * $y3 / $height / 3;
            
            $an[$x] = $y4;
            $as[$x] = $y5;
            
            if($y4>$y4_max)$y4_max = $y4;
            if($y5>$y5_max)$y5_max = $y5;
        
        }
        
        for($x=0; $x<$width; $x++)
        {
            $as[$x] = $height - $y5_max - 1 + $as[$x];
            
            for($y=0; $y<$height; $y++)
            {
            
                imagesetpixel($image, $x,
                    captchaSteroid::_y($y, $an[$x], $as[$x], $height),
                    imagecolorat ( $orig, $x, $y )
                );
            }
    
        }
 
    }
    
    function _sin($x, $f, $w, $width,$height)
    {
    
        return (int) ($height/2)*( 1 - sin($f * 2 * M_PI * ( $x + $w ) / $width ) );
    
    }
    
    function _y($y, $b1, $b2, $height)
    {
    
        return (int)  $b1 + ($y / $height * ($b2- $b1));
    
    }
 
    function _color($hex, &$image)
    {
        if(is_int($hex)) return $hex;
        return ImageColorAllocate($image, hexdec("0x".substr($hex,0,2)), hexdec("0x".substr($hex,2,2)), hexdec("0x".substr($hex,4,2)));
    }
 
}
?>


Код класса мы трогать не будем, напишем скрипт, который будет отвечать за генерацию изображений. Скрипт должен возвращать изображение и соответственно никакого другого вывода, там быть не должно.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php require("class.captchaSteroid.php"); // подключаем файл с описанием класса
 
$captcha = new captchaSteroid();// вызываем конструктор
$captcha->config['font'] = $_SERVER['DOCUMENT_ROOT']."/path_to_fonts/verdana.ttf"; // задаем шрифт, который будем использовать
$captcha->drop(generator()); // выводим изображение,  generator() - функция, возвращающая нужный текст, для картинки
 
function generator()
{
    $db = new db(); // подключаем класс для работы с базой данных
    $ses = $_GET['ses'];//получаем случайное значение, для формирования ключа
    $keys = array(); //задаем набор символов, которые будем использовать
    $str = "";
    $mt = rand(3,5);//случайным образом выбираем, сколько символов будем генерировать
    for($i=0;$i<$mt;$i++){
        $str = $str.$keys[rand(0,26)];//собственно собираем, строчку из случайно выбраных символов
    }
    $key = md5($str.$ses); // формируем Ключ3
    $query = "INSERT INTO captcha(`key`,`timestamp`) VALUES('$key',".time().")";//записываем Ключ3 в базу данных, 
    $db->Query($query);//отправляем запрос к базе
    $cut = time()-(60*60*24*2); //отматываем время на 2-е суток назад.
    $query = "DELETE FROM captcha WHERE `timestamp`<$cut";//и удаляем все ненужные ключи
    $db->Query($query);
    return $str;//возращаем сгенерированную строку
}?>


Класс для работы с базой описан тут
Пример упрощенный, по этому, шрифт используется статический, его можно так же выбирать случайным образом. Если вы используете весь набор символов, то можно выбирать случайным образом, по кодам символа, а не задавать массивом. Вставка сгенерированного изображения будет выглядеть как:

<img src="/path_to_script/sam_script.php?ses=0124124120">


Картинка получена, теперь создадим форму для заполнения.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$ses = time();// берем текущее время и выводим форму
?>
<form action="etazhe_stranica" method="POST">
    <textarea name="coment"></textarea>
    Введите изображенный на картинке текст <img src="/path_to_script/sam_script.php?<?=$ses;?>">
    <input type="text" name="code">
    <input type="submit" value="Отправить">
    <input type="hidden" name="ses" value="<?=$ses;?>">
</form>
 
 
 
?>


Форма тоже есть. Осталось сделать проверку введенной информации, тут все довольно просто. Напишем функцию, которая будет полчучать значение введенных ключей, и возвращать TRUE если такие есть в базе данных.

<?php
function validate($ses,$code){
    $search = md5($code.$ses);//формируем ключ для поиска по базе
    $db = new db()
    $query = "SELECT count(`key`) as cnt FROM captcha WHERE `key`='$search'";//смотрим есть ли такой ключ
    $count = $db->getResult($query,'cnt');
    if($count>0){return true;}// если есть возвращаем true
    return false;//если нет false
}
?>


Собственно все, остается собственно использовать по прямому назначению.

Regards

Спасибо большое за предоставленный класс:
Dragan Bošnjak

Класс был найден тут:
www.phpclasses.org

Опубликовано: 2008-06-28 12:38:09 ShadX

Комментарии

purple_m0nkey Хорошая работа!
Юрий Спасибо, алгоритм пригодился. Я в функцию validate вставил еще удаление ключа в случае успешной проверки. Иначе если бот каким-то образом подберет пару ключей, то сможет ею пользоваться в течение 2 суток.

Оставить комментарий:

Имя:
Почта:
Комментарий:
Что написанно на картинке;)
[:NetFAQ://]

Справочники
[:NetFAQ://]


FreeSoftware
Графика
Аудио&Видео
CD&DVD
Офис
Системные утилиты
Антивирусы
Игры
Разное
[:NetFAQ://]


Магазин
Вавилон 5 - второй сезон
Вавилон 5 - второй сезон
[:NetFAQ://]


Статистика


[:NetFAQ://]


FAQ Новости Блог RSS Задать вопрос