Блог


Каптча из картинок

Задали вопрос:

 

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

 

Есть способы и без каптчи, но об этом в другой раз. Просто хочу показать, что и её не стоит особо бояться. Каптча каптче рознь. Конечно, если это наварот в плане reCAPTCHA, то от неё сразу хочется убежать и спрятаться. Но если превратить каптчу в игру, облегчив донельзя задачу юзеру и привнеся какой-то элемент развлечения, то ни кого она особо и не отпугнет. 

Может кто-то уже увидел, какая стоит каптча у нас на комментариях к блогу для незарегистрированных гостей. А не видели, я покажу:

 

Хотите себе такую? Да пожалуйста.

 

А кому интересно, немного расскажу, как устроено и как сделать самому.

Сначала расскажу немного про каптчу. Вообще невозможно сделать идеальную защиту. Всё, что сделано на компьютере, этим же компьютером и ломается. Текстовые каптчи щелкаются на раз, так как давно придуманы алгоритмы распознавания текстов, как бы зашумлен он не был. Иногда они распознают даже то, что не может человек. Соответственно текстовая каптча спасает только от ботов-поделок третьеклассников.

Сейчас пошла тенденция делать каптчи графические. Допустим искажается изображение и нужно выбрать исходное. Это уже посложнее, особенно если искажение серьёзное, а не просто шум. Допустим скручивание или вот такое:

 

однако и такие алгоритмы есть. Да и вариантов перебора у них не особо много.

Я тут совместил эти два принципа. Передавать нужно текст, а это много вариантов, но читать и писать ничего не нужно. Такая каптча годится даже для детей, не умеющих читать.

 

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

 

Прелесть сей каптчи в индивидуальности. Нельзя придумать алгоритм, который сломает её универсально. Ну или крайне сложно по меньшей мере. Стоит поменять картинки на новые и вуаля.

 

Первым делом нам нужно надергать из сети этих картинок. Или сразу маленьких или отресайзить до размера 100Х100 пикселей. Их нужно чем больше, тем лучше. Важно, что бы они были распределены по тематике и в каждой теме одинаковое число. Допустим 8 котов, 8 слонов, 8 вертолетов, 8 фотоаппаратов и так далее. Не найдете, я дам свои, мне не жалко.

 

Размещаем каждую тему в своей директории под номерами. А все эти директории складываем в следующую папку, это будет группа или категория. А их уже в папку img. Ну а саму ту папку в папку kcaptcha. Вот такая должна получиться иерархия:

 

 

 index.php это как бы наш сайт, на который мы хотим поместить каптчу. 

 

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

 

А теперь немного разберем алгоритм. Первым делом выбираем случайное число. Это будет группа картинок, в которые нужно потыкать мышкой. Таких картинок должно быть не меньше трех, иначе будет легко взломать каптчу простым перебором. Значит помещаем три таких числа в массив. Потом добиваем массив случайными числами. Перемешиваем его и формируем блок с кнопками-картинками, присваивая им определенные символы из рaндомной строки. А если выбранное "счастливое" число совпадает с текущим элементом массива - записываем символ из случайной строки в сессию. Потом сверяем сессию с тем, что натыкал юзер.

Сложно? Ну потом внимательно посмотрите листинг, там все подробно раскомменчено.

Нам осталось только выдать картинки в нужном порядке и сформировать картинку-эталон. За это отвечают два других файла - big_img.php и small_img.php Я думаю не стоит объяснять, где большая, а где маленькие картинки.

Еще каптчу можно настроить, если захочется добавить других групп картинок. Настройки как обычно в конфиге. А так же сверстать свой шаблончик. Он соответственно в файле kchaptcha.tpl

Ну а вот и сам листинг. Разбирайтесь. А можно не разбираться, просто скачать и поставить. Это очень просто, в индексе показано как.

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


// Обязательно запускаем сессию
    
session_start();
    
///////////////////////////////////////////////////    
    // Подключаем основной скрипт
    
include './kcaptcha/kcaptcha.php';
    
// Проверка. 
    
$kcaptcha_access checkKcaptcha();
///////////////////////////////////////////////////

    // А тут уже производим основное действо
    
if(!empty($_POST['ok']) && $kcaptcha_access)
        echo 
'<b style="color:green">Молодец. Возьми с полки пироженку.</b>';
    elseif(!empty(
$_POST['ok']))
        echo 
'<b style="color:red">Пошел вон, злобный спамер и тролль!</b>';
        
?>

<form action="" method="post">
<!-- Подключаем в нужное место шаблон каптчи -->
<?php include './kcaptcha/kcaptcha.tpl'?>
<input name="ok" type="submit" value="Пуск!" />
</form>

 

kcaptcha/config.php

1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
<?php

//////////////////////////////
// Настройки каптчи
//////////////////////////////

// Путь http до директории с каптчей
    
define('KCP_PATCH''/kcaptcha');
    
// Количество категорий
    
define('KCP_NUM_CATEGORY',  2);
    
// Количество картинок в папке   
    
define('KCP_NUM_IMG'8);

 

kcaptcha/kchaptcha.tpl

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
<script type="text/javascript" language="javascript">
    function kcaptcha(b)
    {
        var a = document.getElementById('kcaptcha').value;
        document.getElementById('kcaptcha').value = a + b;
    }

</script>

<table width="350px" border="0" cellpadding="2" style="font-size:14px">
  <tr>
    <td>Защита от роботов. <br />Найдите <b>всех</b> подобных животных<br />
     </td>
    <td>
     </td>
  </tr>
  <tr>
<td valign="middle" style="width:140; 
                           text-align:center;
                           height:140;
                           background-color:#7e7e7e">
<img src="
<?php echo KCP_PATCH?>/big_img.php" /><
    </td>
    <td>
<?php 
echo createKcaptcha(); ?></td>
  </tr>
    <tr>
    <td colspan="2">
    <input id="kcaptcha" name="kcaptcha_key" type="text"/>
    <input  
       onclick="document.getElementById('kcaptcha').value=''"
       type="button" value="Очистить" />
    </td>
  </tr>
</table>

 

kcaptcha/kcaptcha.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
 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
<?php

 
    
include dirname(__FILE__) .'/config.php';
/**
* Функция генерации каптчи
* @return string
*/
    
function createKcaptcha() 
    {
    
// Очищаем переменные сессии, при каждом заходе новые
        
unset($_SESSION['kcaptcha_look']);
        unset(
$_SESSION['img']);
     
        
$links '';
    
// Выбираем случайную категорию
       
$_SESSION['category'] = mt_rand(1KCP_NUM_CATEGORY);
    
// Выбираем "счастливую цифру" и помещаем в сессию.
    // Она нам понадобится еще и позже    
       
$_SESSION['fold'] = $bingo mt_rand(16);
    
// Помещаем 3 штуки в массив, чтобы не менее 3 вариантов
        
$arr array_fill(03$bingo); 
    
// Дополняем массив случайными числами
        
for($i 3$i 10$i++)
            
$arr[$i] = mt_rand(16);
    
// Кручу-верчу, всех запутать хочу    
       
shuffle($arr);
    
// Формируем массив символов
        
$letters range('A''Z');
        
$random_string array_merge($letters$letters); 
    
// Взбалтываем
        
shuffle($random_string); 
    
// Начинаем формировать блок с кнопками-картинками   
        
for($i 0$i 9$i++)
        {   
// выдергиваем из массива по 1 случайному символу
            
$let $random_string[$i];
            
// Если выпала "счастливая цифра"
            
if($bingo == $arr[$i])
            {   
// Запоминаем в сессию её порядок
                
$_SESSION['img'][] = $i 1;
                
// И текущую букву

                
$_SESSION['kcaptcha_look'][] = $let;
            }
            
// По 3 картинки в ряд
            
$br = (($i 1) % == 0) ? '<br>' NULL;
            
// Собираем блок кнопок-картинок
            
$links .= '<a href="javascript:kcaptcha(\''$let .'\')" '
                   
.  'style=" font-weight:bold;text-decoration:none" >'
                   
.  '<img title="'$let .'" alt="'$let .'"  '
                   
.  'src="'KCP_PATCH .'/small_img.php?num='. ($i 1) .'" '
                   
.  'style="width:50px; height:50px" border="0" /></a>'
                   
.  $br;    
        }
        
// Готово
        
return $links;
    }  

/**
* Функция проверки каптчи
* @return string
*/    
    
function checkKcaptcha() 
    {    
        if(!empty(
$_SESSION['kcaptcha_look']) && !empty($_POST['kcaptcha_key']))
        { 
//Разбиваем строку ключа в массив
            
$kcaptha_key str_split($_POST['kcaptcha_key']);
          
// Сортируем оба массива
            
sort($_SESSION['kcaptcha_look']);
            
sort($kcaptha_key);
          
// Сверяем прямо влоб. Если ok, меняем флаг доступа
            
if($_SESSION['kcaptcha_look'] == $kcaptha_key)
                return 
true;
        } 
        
        return 
false;
    }   
    

 

kcaptcha/big_img.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
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
<?php


    
// Подключаем конфиг
    
include dirname(__FILE__) .'/config.php';
   
// Сессия в каптче обязательна
    
session_start();  
   
// Выбираем рандомную картинку из группы
    
$imd      mt_rand(1KCP_NUM_IMG);
    
$filename dirname(__FILE__) .'/img/'. @$_SESSION['category'
              .
'/'. @$_SESSION['fold'] .'/'$imd .'.jpg'
    
// Устанавливаем случайный угол поворота
    
$degrees  mt_rand(-9090);
    
// Cлучайный размер
    
$size     mt_rand(6090); 
    
// Случайное искажение
    
$w $size mt_rand(310); 
    
$h $size mt_rand(310);     
    
// Вынимаем картинку
    
$im imagecreatefromjpeg($filename); 
    
// Немного фильтров
    
imagefilter($imIMG_FILTER_EMBOSS);
    
imagefilter($imIMG_FILTER_GRAYSCALE);
    
// Изменяем размер
    
$thumb imagecreatetruecolor($w$h);
    
imagecopyresized($thumb$im0000$w$h100100 ); 
    
$col   imagecolorallocate($thumb126126126); 
    
// Поворачиваем
    
$thumb imagerotate($thumb$degrees$col);
    
$x     imagesx($thumb);
    
$y     imagesy($thumb);  
    
$ims   imagecreatetruecolor($x$y); 

    
imagecopymerge($ims$thumb0000$x$y100); 
    
// Выдаем наружу
    
header ("Content-type: image/png"); 
    
imagepng($ims); 

 

kcaptcha/smal_img.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
 26
 27
 28
 29
<?php


    
// Подключаем конфиг
    
include dirname(__FILE__) .'/config.php';
    
// Не забываем про сессию
    
session_start();
    
// Номер кнопки-картинки
    
$num  = @$_GET['num'];
    
$fold = @$_SESSION['fold'];
    
// Если это не "счастливая" директория
    
if(!in_array($num$_SESSION['img']))
    {
// Выбираем любую другую
        
$arr range(06);
        unset(
$arr[0], $arr[$fold]);  
        
$fold array_rand($arr);         
    }
    
// Формируем путь до случайной картинки в выбранной директории 
    
$filename dirname(__FILE__) .'/img/'. @$_SESSION['category']               
              .
'/'$fold .'/'mt_rand(1KCP_NUM_IMG) .'.jpg';
    
// Цоп её из папки
    
$thumb    imagecreatefromjpeg($filename);
    
$im       imagecreatetruecolor(5050); 
    
imagecopyresized($im$thumb00005050100100); 
    
// И отправляем восвояси.
    
header ("Content-type: image/png"); 
    
imagepng($im); 
    
imagedestroy($im); 

 

Всё)) Но пасаран! Злобный спамер, го хом!

 

Николай aka twin

 

Теги: PHP | HTML

Комментарии (23)

kdes70
23-03-2013
Что то не пойму почему то у вас в примере не выподают выриантов с нужной картинкой в сером окошке одно а в блоке с вариантами ее нет, перегрузыв страницу появляется одна (похожая) нажимаю но срабатывает защита, как то не понятен принцип ее работы сколько раз нужно нажимать на наеденое совпадение???
kdes70
23-03-2013
мне кажется для нее нужен AJAX
twin
23-03-2013
Аякс для каптчи? Это что то новое.
twin
23-03-2013
Нужно искать не такую же картинку, а подобные. Мишек или слонов.
kdes70
23-03-2013
а порядок не важен? как я понимаю для каждой картинки формируется уникальная буква, AJAX я имел введу отправку результата на сервер.. теесть проверку чтобы моментально полючать, или я глупость говорю??
kdes70
23-03-2013
вот ща один рас получилось! но в серой не изменилась картинка для повторного выбора, а в вариантах поменялась, и получилось что в серой осталась таже птича, а вариантах появилась только одна. и не срабатывает
twin
23-03-2013
Нашел опечаточку досдную. Исправил сейчас, больше глючить не должно.
kdes70
23-03-2013
может чтобы не путались люди с порядком нажатия на картинки стоит их вывести в линию??
twin
23-03-2013
Это кто как хочет. Хотя порядок там не важен.
Fable92
23-03-2013
Благодарю за эту полезную статью
Savage
24-03-2013
Встречается капча, в которой надо дособрать картинку из недостающих кусочков их перетаскиванием на неполное изображение предмета. Как такая делается интересно?
twin
24-03-2013
В уроке про DOM есть пример drag$drop. Просто высчитываются координаты фрагмента и передаются на сервер. А там сравниваются с заранее заданными. Ничего сложного.
Savage
25-03-2013
Принцип ясен, спасибо.
Savage
25-03-2013
Во, как раз сегодня, бот как я понял, кидал в гостевую каждые 4 минуты какую-то ерунду не через форму (т.к., если отключить кнопку "Отправить" тоже самое было), а через адресную строку. капчу решил не ставить, а просто по содержимому сообщения (на наиболее встречающееся слово) фильтр поставил. две строчки кода и тишина пока. Интересно, такой способ без капчи используется в боевых условиях?
Полезная тема... )))
Savage
26-03-2013
А ещё можно передачу значения из скрытого поля формы сделать, тогда в обход формы добавлять не получится сообщения.
pamparam
26-03-2013
что то подобное тут уже слышал :)

странно смайлы не активны
Fable92
15-08-2013
Нельзя помещать значение каптчи в переменную $_SESSION['kptcha_look'] ибо если открыть сначало одну вкладку с формой, и открыть другую в этом же браузере , то значение $_SESSION['kptcha_look'] очистится и перезапишется , следовательно с предыдущей вкладки нельзя будет отправить комментарий с чем я и столкнулся на своем сайте , лучше использовать что-то наподобие массива с уникальным номером (я использовал время создания формы + случайное число), то - же самое у вас касается и прошивки формы , переменной $_SESSION['filim']
P.s Разьясните если я неправ, буду благодарен
twin
15-08-2013
Вобщем то это частный случай, но поправка не лишена смысла конечно.
Рома
30-03-2015
Не отображаются картинки, в чем может быть проблема?
марк
30-03-2015
Аналогичная с Ромой проблема, не отображаются картинки.

нашел ошибку используется картинки jpeg и функции jpeg а вывод png.
картинки не заработали
антонио
06-10-2015
О, я для такой уже бота писал, чтоб проходил. А тут еще и исходники есть, вообще прелесть
twin
07-10-2015
Замок служит для того, чтобы его открывать. Если бы этого функционала было бы не нужно, просто дверь забить гвоздями или сваркой заварить.

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

Дерзай!
Роман Б
14-10-2015
Чисто попробую.

 
Наверх