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

Капча (captcha) представляет из себя изображение с некоторым текстом, который предлагается набрать пользователю в поле ввода для подтверждения его "человечности". Дело в том, что человеку одинаково легко читать текст, независимо от того, является ли он непосредственно машинным текстом или изображением. В то время как компьютеру требуется гораздо более сложный алгоритм для "чтения" текста в виде картинки. Очевидно, что капча каждый раз должна отображать случайную последовательность символов.

Капчи используются, как правило, при заполнении каких-либо форм на сайте. Алгоритм работы следующий: на форме присутствует изображение-captcha с некой случайной последовательностью символов. Рядом с ним имеется поле для ввода содержимого капчи пользователем. Изображение, по сути, является PHP-скриптом, который его формирует. При этом сгенерированное текстовое содержимое капчи где-то сохраняется. При отправке формы скрипт сравнивает сохраненное значение капчи с тем, что ввел пользователь. Если значения совпадают, то запрос принимается, иначе отклоняется.

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

Обратите внимание, что для работы скрипта требуется PHP-расширение GD, подключите его в настройках.

Для начала необходимо определиться со шрифтом. Соответствующий выбранному шрифту ttf-файл необходимо положить в директорию с будущим скриптом капчи. Я для капчи выбрал шрифт Comic Sans MS , ему соответствует файл comic.ttf . Файлы шрифтов можно найти в системном каталоге шрифтов вашей операционной системы, либо загрузить из Интернета.

$letters = "ABCDEFGKIJKLMNOPQRSTUVWXYZ" ;

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

$caplen = 6 ;

В этой переменной задается длина капчи (6 символов).

$width = 120 ; $height = 20 ;

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

$font = "comic.ttf" ;

Здесь указывается файл шрифта. В принципе, его можно разместить в поддиректории, допустим, fonts, тогда содержимое переменной должно быть такого формата: fonts/comic.ttf .

$fontsize = 14 ;

Размер шрифта.

С переменными разобрались. Далее, приступаем непосредственно к реализации. Во-первых, необходимо указать клиенту запрошенного скрипта, что ответный контент представляет из себя не текст, а изображение. Для этого переопределяем содержимое HTTP-заголовка content-type:

header ("Content-type: image/png" ) ;

Создаем изображение с заданными размерами:

$im = imagecreatetruecolor ($width , $height ) ;

Выставляем флаг необходимости сохранения альфа-канала изображения:

imagesavealpha ($im , true ) ;

Создаем цвет фона. Это будет полностью прозрачный цвет:

$bg = imagecolorallocatealpha ($im , 0 , 0 , 0 , 127 ) ;

Заливаем этим цветом наше созданное изображение:

imagefill ($im , 0 , 0 , $bg ) ;

Этими действиями мы подготовили наше изображение для наложения на него капчи. Следующая строка необходима не всегда, зависит от настроек веб-сервера. Она переопределяет путь к поиску шрифтов. Оставьте ее закомментированной, если вдруг капча не будет формироваться при тестировании, а в логе ошибок Apache будет появляться ошибка "imagettftext(): Could not find/open font" , попробуйте ее раскомментировать:

//putenv("GDFONTPATH=" . realpath("."));

Инициализируем переменную, в которой будет содержаться текстовое значение капчи:

= "" ; for ($i = 0 ; $i < $caplen ; $i ++ )

На каждом шаге цикла генерируется очередной символ капчи и рисуется на изображении. Берем случайный символ из нашего алфавита и добавляем его в капчу:

.= $letters [ rand (0 , strlen ($letters ) - 1 ) ] ;

Вычисляем положение сгенерированного символа на изображении по оси x:

$x = ($width - 20 ) / $caplen * $i + 10 ;

Это положение зависит от ширины изображения, длины капчи и порядкового номера символа. Далее мы добавляем немного "случайности" в это положение:

$x = rand ($x , $x + 4 ) ;

Вычисляем положение сгенерированного символа на изображении по оси y:

$y = $height - ( ($height - $fontsize ) / 2 ) ;

Положение зависит от размера шрифта и высоты изображения.

Генерируем случайный цвет для символа. Этот цвет не должен быть слишком светлым, поэтому каждый из компонентов цвета (R, G и B) генерируем в диапазоне 0-100:

$curcolor = imagecolorallocate ( $im , rand (0 , 100 ) , rand (0 , 100 ) , rand (0 , 100 ) ) ;

Для текущего символа случайным образом генерируем его угол наклона в диапазоне -25..25 градусов, чтобы буквы на капче "плясали":

$angle = rand (- 25 , 25 ) ;

И, наконец, рисуем символ со всеми выше полученными характеристиками на изображении:

imagettftext ($im , $fontsize , $angle , $x , $y , $curcolor , $font , [ $i ] ) ;

На этой строке оканчивается тело цикла. Когда цикл отработает, в переменной будет содержаться текстовое значение капчи, а изображение im будет представлять из себя отрисованную капчу. Необходимо где-то сохранить значение капчи, чтобы основной скрипт, который использует ее, мог проверить значение пользователя. Лучшее для этого место - сессионная переменная. Инициализируем сессию и сохраняем значение капчи:

session_start () ; $_SESSION = ;

Наконец, выводим сформированное изображение captcha:

imagepng ($im ) ;

И освобождаем память, выведенную под изображение:

imagedestroy ($im ) ;

Ниже вы можете увидеть демонстрацию работы полученной капчи (попробуйте обновить изображение):

Чуть позже добавлю готовый скрипт капчи и пример его использования в HTML-формах.

Для тех, кто не в курсе: капча - это проверка на человечность. Ни что иное как борьба со спамом. Попытка не допустить автоматических действий роботов. Используется везде, где только присутствуют формы. Отправка комментария, заказ товаров или сообщение администратору, добавление контента (при количестве авторов > 1), регистрация, иногда даже вход в защищённую зону - все эти нежные места сайта охраняются ею (капчей).

Вам доводилось вводить кривенькие циферки с буковками при регистрации на форуме или при публикации комментария? Тогда вы знаете, что такое капча.

Немного истории
Почему «капча»? Ок. На самом деле это всего лишь русское произношение английского слова CAPTCHA - Completely Automated Public Turing test to tell Computers and Humans Apart, что переводится как «полностью автоматизированный публичный тест Тьюринга для различия компьютеров и людей».

Итак, о чём мы. Капча бывает разная (удержусь от перечисления видов). На счёт эффективности и степени раздражения пользователя можно спорить долго. Кому-то нравится вводить буковки, кому-то - дёргать рычажки, кому-то - решать задачки, кому-то - кликать по чекбоксу. Однако все согласятся, что если бы капчи не было вообще, им стало бы комфортней. Поэтому следует делать её как можно незаметней.

Оригинальному проекту - оригинальная капча
Создавая свой оригинальный проект, никак не хочется вешать на него стандартные решения в виде кривых буковок или математических задач. К тому же, все эти «хитрые» алгоритмы давно распознаются «умными» роботами. Поэтому «защита» из них хреновая.

Очень важную роль играет простота капчи. Чем она сложнее, тем меньше желания у пользователя оставлять комментарий, регистрироваться или делать заказ. Далее рассмотрим несколько простых, но эффективных методов защиты от спама, которые не раздражают пользователя.

Капча «Я не робот»
Очень простое решение защиты от спама в виде чекбокса под названием AmCaptcha реализовал Алексей Московский (изначально идея Dimoning"а) в виде плагина для WordPress. Идея до безобразия проста - при клике по чекбоксу небольшой javascript изменяет значение скрытого поля , а потом при обработке формы сервер проверяет это значение if($_POST["antispam"]==="spam") exit("да это эже спам!"); .

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

Это решение имеет только один заметный минус - работа только при включённом javascript у пользователя. Однако это можно доработать при желании. Об этом я расскажу чуть позже.

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

Этот метод можно использовать в любом месте сайта. Он не особо надоедает визуально, но требует места в форме. К тому же пользователь должен понимать, что это капча.

Капча на основе Confirm
Ещё более простой и менее надоедливый метод капчи - это использование javascript события confirm. Вы же используете javascript валидацию форм на своём сайте? ;) Просто добавьте ещё одно условие.

Должно получиться что-то типа того:

function validator(form) {
if (form.name.value==="") { alert("Пожалуйста, укажите Ваше имя."); form.name.focus(); return false; }
if (form.email.value==="") { alert("Вы должны указать свой email адрес."); form.email.focus(); return false; }
if (form.text.value==="") { alert("Вы не заполнили поле сообщения."); form.text.focus(); return false; }
if (confirm("Проверьте ваши данные перед отправкой, чтобы мы могли с вами связаться. Всё верно?")) form.nospam.value="itsnospam"; else return false;
return true;
}

Как всегда всё чрезвычайно просто. При нажатии всплывает формочка с текстом «Проверьте ваши данные перед отправкой, чтобы мы могли с вами связаться. Всё верно?» и вариантами ответа «Ок» и «Отмена». Пользователь может нажать «Отмену» и действительно проверить введённые данные (ещё один плюс). Имхо это очень удобно. Причём пользователь даже и не подумает, что это капча. Ведь это всего лишь напоминание о правильности данных.
Этот метод строится также, как и предыдущий, на изменении скрытого поля, которое проверяется при обработке формы. И точно также серьёзно стоит подойти к генерации значения.

Этот метод более элегантный, но имеет свои недостатки. Всплывающее окошко быстро надоедает, поэтому его нельзя использовать там, где посетитель пользуется формами часто (например, отправка комментариев). Его стоит использовать в формах заказа и отправки сообщения администратору. Зато он не требует места в форме и не похож на капчу. А также эта капча повышает внимание к правильности заполнения всех полей.

Если JavaScript отключён
Не стоит пренебрегать пользователем, у которого отключен javascript. Это касается обоих описанных методов защиты от спама. Я предлагаю сделать дополнительную обработку и в случае отключённых скриптов отправлять посетителя на страницу с подтверждением человечности. Вот тут можно использовать и классическую капчу с циферками и задачками. Ведь очень немногие пользователи увидят эту страницу. Или можно не использовать капчу вообще - роботы ведь не должны попасть на эту страницу.

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

Механизм очень прост. В форму добавляется кнопка «Предпросмотр», при нажатии которой происходит обработка формы и вывод на экран введённых пользователем данных. Кнопку «Отправить» при этом следует делать неактивной до наступления события предпросмотра.

Как вы уже наверно догадались, при предпросмотре происходит не только разблокировка кнопки «Отправить» (от роботов она ведь не блокируется). Также изменяется какой-то параметр. Например, скрытое поле. А затем производится проверка на сервере.

Огромный плюс этого метода в том, что все обработки происходят на сервере вне зависимости от жаваскрипта. Что более безопасно и защищено от анализа спамерами.

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

Послесловие
Будьте оригинальны. Экспериментируйте и создавайте свои методы надёжной и ненавязчивой капчи. Ведь разным проектам подойдут совершенно разные методы. К тому же, не стоит все формы (если у вас много разновидностей таковых) обрабатывать одинаково. Посетитель должен быть доволен.

Сегодня, благодаря господам спам-ботам и иже с ними, в сети практически невозможно что-либо сделать без ввода символов со сгенерированного изображения - CAPTCHA (далее - капча) . Фактически, это предотвращает выполнение какого-либо скрипта без участия человека, и в этом топике я расскажу, как такую капчу создать средствами PHP, а так же упомяну очень полезный топик другого хабраюзера, который пригодится при разработке изображения-капчи.

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

Основные правила При разработке капчи нужно обязательно соблюдать несколько основных правил:

1. Капча создана для людей
Она должна сходу читаться, но не в ущерб стойкости к распознаванию. Очень наглядный пример капчи, не соблюдающей это правило - изображение справа.

2. Генератор капчи должен быть четко ограничен в используемых символах
Наглядный пример - изображение в начале топика. Конечно, reCAPTCHA - чудесное изобретение, но порой она предлагает ввести символы, которые и в charmap найти сложно. Кстати, когда речь идет о капче с использованием кириллических символов - ни в коем случае генератор не должен использовать букву «ё». Лично я знаю много людей, у которых на ~ (тильда / ё) повешено какое-то действие в системе.

3. Капча должна быть стойкой к распознаванию
… но не в ущерб читаемости. Вообще, этот пункт - самый сложный во всей разработке. Необходимо найти золотую середину - капча сходу читается людьми и вообще (насколько возможно) не читается ботами. Так же необходимо учитывать специфику ресурса, на котором планируется использовать капчу, и его контингента. Если речь идет о, допустим, форуме читающих домохозяек за сорок, то на стойкость капчи можно плюнуть с высокой башни - никому он не упрется. Если речь идет о, например, имейджборде, то тут нужна капча а-ля вырвиглаз.

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

Итак, по порядку перечислим, что же мы можем использовать:

1. В изображении должно использоваться хотя бы несколько цветов. Желательно всегда разные


Изображение выше - пример того, как это выглядит в действии. На самом деле, это не очень надежный вариант, так как текст с фоном очень контрастирует. Разберемся с цветами позже.

2. Должен присутствовать шум


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

3. Буквы должны находиться на небольшом расстоянии друг от друга


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

4. Размеры символов должны быть разными


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

5. Отвратительный шрифт


Очень полезный способ. Засечки, курсив, стилизация - отличные подводные камни для бота. Так же в сочетании с шумом в виде линий очень хорошо будет смотреться тонкий шрифт. Если абстрагироваться от первого правила генерации капчи, то можно использовать множество шрифтов сразу, к примеру свой шрифт для каждого символа.

6. Символы под случайным углом

Весьма действенный способ оградиться от ботов. Опять же, сегментация будет усложнена, хоть и не значительно. Лучше всего выбрать небольшой диапазон угла, иначе же читаемость сильно испортится (буквы будут наплывать друг на друга).

7. Динамические искажения

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

Итак, что же мы сделаем:
- Контрастный фон, с шумом
- Линии за текстом, линии на тексте
- Текст в случайном положении
- Количество символов будет случайным, от 4 до 7
- Размер каждого символа будет случайным
- Цвет текста каждый раз будет случайным
- Символы будут слегка прикасаться
- Каждый символ будет под случайным небольшим углом

Разработка Определимся с целью:
- Генерация шума
- Генерация текста
- Форма с возможностью обновления капчи
- Обработчик введенных данных
По мере написания статьи понял, что искажения в данном случае абсолютно неуместны. Для тех, кому они все же нужны - в конце топика ссылка на урок по созданию искажений.Пишем форму Обновить капчу Введите капчу:
Тут все предельно ясно, но на всякий случай я откомментировал некоторые строчки. Кстати, обратите внимание, я не установил параметр maxlenght у поля ввода. В большинстве случаев, разработчики устанавливают этот параметр, указывая размер капчи. Во-первых это нехилая такая подсказка для бота, во-вторых у нас количество символов будет динамическим. Все, интерфейс мы написали, пора приступать к созданию скрипта генерацииПишем генератор кода капчи (random.php)
Тут все ограничивается только вашим воображением. Как-то я видел иной способ - выбиралась текущая минута, час, месяц, все это перемножалось, из этого выбирались 10 случайных символов, дважды извлекался MD5-хеш, из него извлекались 6 случайных символов и затем все это еще и перемешивалось. Кстати, обратите внимание на выбранные мною символы - я исключил такие как i, l, 1 и 0, o, c, из-за того, что они слишком похожи друг на друга, в некоторых ситуациях пользователь может ошибиться. Генератор я назвал random.php (далее он будет запрашиваться в других скриптах).Пишем генератор изображения (captcha.php)