Рейтинг@Mail.ru
 

Капча

Очень часто при регистрациях или просто каких то активных действиях нам предлагается ввести в поле код, нарисованный на картинке.
Вот это и есть так называемая CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) или по русски - капча.

Для чего это нужно.
Дело в том, что при автозаполнении формы (роботом или ботом, как сейчас принято говорить), у спамеров возникает трудность - распознать эту надпись. У робота нет глаз и ассоциативного мышления, он может только провести попиксельный анализ картинки. Это далеко не всегда бывает успешным, так как создаются всевозможные помехи и искажения, которые человеческий мозг, в силу своей продуктивности, спокойно отсеивает, а глупый бот не может.

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

Правда нужно отдавать себе отчет в том, что сложная капча на форме регистрации по статистике отпугивает до 70% потенциальных пользователей. Но иногда это бывает действительно необходимо.

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

Начнем со скрипта, строющего эту самую картинку с несколькими буквами/цифрами (в нашем случае их будет 5).

Алгоритм создания капчи можно представить в виде:
1. Создать некую строку из случайных символов.
2. Создать пустое изображение или загрузить фон из файла.
3. Нарисовать на этом изображении помехи (случайные точки, линии).
4. Написать строку на этом изображении.
5. Сохранить эту строку в сессии (позднее будет использован для проверки, верно ли ввел пользователь код с картинки).
6. Показать изображение.

Код будет полностью соответствовать данному алгоритму.
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
<?php

// Начинаем сессию, далее мы добавим в нее ключ 
    
session_start(); 

// Получаю строку из пяти случайных символов 
    
$string getRandomString(5'lower'); 

// Задаем размеры капчи (самой картинки) в пикселях 
    
$width 108
    
$height 25

// Строим пустое изображение. К высоте добавляется 15 пикселей, это будет белая 
// полосочка снизу с текстом "press to change" 
    
$captcha imagecreatetruecolor($width$height 15); 

// Получаю случайный цвет для фона 
    
$bg imagecolorallocate($captchamt_rand(1050), mt_rand(1050), mt_rand(1050)); 

// Определяю цвет фона для строки 
    
$font_color imagecolorallocate($captchamt_rand(220255), mt_rand(220255), mt_rand(220255)); 

    
$white imagecolorallocate($captcha255255255); 
    
$black imagecolorallocate($captcha000); 

// Заливаю капчу цветом фона, но оставляю снизу белую полоску 
    
imagefill($captcha00$white); 
    
imagefilledrectangle($captcha00$width$height$bg); 

// "Разбрасываю" в случайном порядке четыре линии по капче. Цвета линий тоже 
// определяются случайно 
    
for ($i 0$i 4$i++)  
    { 
    
// Создаю случайный цвет для очередной линии 
        
$color imagecolorallocate($captchamt_rand(170255), mt_rand(170255), mt_rand(170255)); 

    
// Черчу линию между двумя случайными точками 
        
imageline
                   
$captcha,  
                   
mt_rand(0$width  1),  
                   
mt_rand(0$height 1),  
                   
mt_rand(0$width  1),  
                   
mt_rand(0$height 1),  
                   
$color 
                   
); 
    } 

// Пишу эту случайную строку в капче 
    
imagestring($captcha5334$string$font_color); 

// Указание пользователю 
    
$how_refresh 'press to change'
    
imagestring($captcha3226$how_refresh$black); 

// Сохраняю строку в сессии 
    
$_SESSION['key'] = $string

// Отсылаем заголовок браузеру, что ему сейчас будет передана картинка 
    
header('Content-type: image/png'); 

// Отсылаем картинку в стандартный выходной поток (в браузер) 
    
imagepng($captcha); 

/** 
 * Генерирует строку случайных символов 
 *  
 * @param int $length  - длина строки 
 * @param string $case - регистр генерируемых символов, может быть lower, upper, 
 *                       both. Если не передан ни один из вышеперечисленных, то 
 *                       используется lower 
 * @return string - строка, состоящая из случайных символов заданной длины 
 *                     
 */ 
    
function getRandomString($length$case 'lower')  
    { 
        
/* Латинские символы, похожие на символы кирилицы: 
         * в ниженем регистре: a b c e o p x l
         * в верхнем регистре: A B C E H K M O P T X 
         */ 
        
$ban_chars = array('a''b''c''e''o''p''x''l'
                           
'A''B''C''E''H''K''M''O''P''T''X'); 

        
// В зависимости от $case формирую массив диапазонов символов, из которых 
        // можно выбирать 
        
switch ($case)  
        { 
            case 
'upper'
                
$random_chars range('A''Z'); 
            break; 

            case 
'both'
                
$random_chars array_merge(range('a''z'), range('A''Z')); 
            break; 

            case 
'lower'
            default: 
                
$random_chars range('a''z'); 
            break; 
        } 
    
// Добавляю цифр (ноль похож на O_o, по этому игнорируем) 
        
$random_chars array_merge(range(19), $random_chars); 
    
// Удаляю неоднозначные символы 
        
$random_chars array_diff($random_chars$ban_chars); 
    
// Перемешиваю массив 
        
shuffle($random_chars); 
    
// Беру первые $length элементов 
        
$chars array_slice($random_chars0$length); 
    
// Соединяю их в строку и - марш на выход. 
        
return implode(''$chars); 
    }


Надеюсь с процессом создания капчи все было понятно. Идем далее, создайте файл index.php и поместите в него следующий код:

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
<?php

    session_start
(); 
// В переменной $status хранится подсказка для пользователя, которая потом будет 
// отображаться в форме 
    
$status 'Введите текст с картинки'
     
    if (isset(
$_POST['submit']))  
    { 
    
// Сравниваем то, что ввел пользователь с ключем в сессии 
        
if (!empty($_SESSION['key']) && ($_SESSION['key'] == $_POST['txtCaptcha']))  
        { 
        
// Тут можно, нет, даже нужно поместить код, который будет выполняться 
        // в случае правильного ввода капчи, например добавить пост в гостевую 
        // книгу 
            
$status 'Правильно!'
        }  
        else  
        { 
        
// Ну а тут, соответственно, что делается в случае ошибки 
            
$status 'Вы ошиблись :-('
        } 
    }


Основная логика уже сделана, ничего сложно в этом не было. Теперь осталось сделать форму.

Вот тут одна изюминка. Дело в том, что иногда бывает действительно трудно прочитать, что же там написано. И по этому нужно дать возможность сменить картинку. Мы сделаем это с помощью JavaScript, просто заменив src у картинки. Но если вдруг скрипты у клиента отключены, то такой возможности не будет.

Так вот такая хитрость. Выведем капчу не в виде простой картинки, а в виде тега <input> тип image.
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
<?php

    session_start
(); 
// В переменной $status хранится подсказка для пользователя, которая потом будет 
// отображаться в форме 
    
$status 'Введите текст с картинки'
     
    if (isset(
$_POST['submit']))  
    { 
    
// Сравниваем то, что ввел пользователь с ключем в сессии 
        
if (!empty($_SESSION['key']) && ($_SESSION['key'] == $_POST['txtCaptcha']))  
        { 
        
// Тут можно, нет, даже нужно поместить код, который будет выполняться 
        // в случае правильного ввода капчи, например добавить пост в гостевую 
        // книгу 
            
$status 'Правильно!'
        }  
        else  
        { 
        
// Ну а тут, соответственно, что делается в случае ошибки 
            
$status 'Вы ошиблись :-('
        } 
    } 

    
$login = !empty($_POST['login']) ? $_POST['login'] : NULL
     
?> 
<html> 
    <head> 
        <title>Капча</title> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
        <script type="text/javascript"> 
            // Функция, обновляющая капчу 
            function refreshCaptcha() { 
                img = document.getElementById('imgCaptcha'); 
                img.src = 'captcha.php?' + Math.random(); 
                return false 
            } 
        </script> 
    </head> 
    <body> 
        <form method="post" action="" > 
            <table width="240" border="0" align="center" cellspacing="8"> 
                <tr> 
                    <!-- Возврат введенных данных   --> 
                    <td colspan="2" align="center"> 
                    <input name="login" type="text" value="<?php echo htmlspecialchars($login); ?>" /> 
                    </td> 
                </tr>             
             
                <tr> 
                    <!-- Показываю подсказку пользователю  --> 
                    <td colspan="2" align="center"><div id="status"><?php echo $status?></div></td> 
                </tr> 

                <tr> 
                    <td> 
                        <!-- Если включен JS, то при щелчке на капчу, последняя будет обновляться --> 
                        <input type="image" id="imgCaptcha" src="captcha.php"  value=""  onclick="return refreshCaptcha();" /> 
                    </td> 
                    <td> 
                        <!-- Окошко, куда вводить текст с капчи --> 
                        текст: <input type="text" name="txtCaptcha" value="" maxlength="5" size="5" /> 
                    </td> 
                </tr> 
                <tr> 
                    <td colspan="2" align="right"> 
                        <input type="submit" value="Проверить" name="submit" /> 
                    </td> 
                </tr> 
            </table> 
        </form> 
    </body> 
</html>  
Это кнопка в виде картинки, работает как submit, то есть отправляет форму. На onClick кнопки повешаем перехват события. То есть если JS включен, то событие отправки формы будет остановлено (return false) и страница перегружаться не будет.
А если скрипты отключены, то форма отправится. А так как она у нас в одном файле с обработчиком, данные вернутся и никто ничего не потеряет.

Вот и всё. Вот так она работает:
Ваше имя
Введите текст с картинки
текст:
Сам скрипт генерирующий капчу довольно прост, можно его усложнить, добавив загрузку фона, вместо создания пустого изображения, можно добавить случайных точек, можно писать текст буквами разных размеров и под разным углом, можно использовать ttf шрифт, можно слово не генерировать случайным образом а выбирать из файла, можно вообще еще много чего придумать на самом деле :-)

Сеин Денис aka DIII