Изначально гостевая книга этого сайта была написанная с верой в идеальность мира
и честность всех в нём живущих ;-) . К сожалению мир не идеален, и не все в нём
честны, потому через некоторое время гостевая оказалась буквально завалена
"горячими" предложениями от самых различных ботов. Разумеется напрашивается
очевидный вывод - нужно поставить CAPTCHA.
Сайт использует Perl, и я стал искать подходящий вариант реализации на этом
языке. Поискав в интернете я не нашёл ничего подходящего моим условиям:
простота использования и настройки, минимум зависимостей от других модулей,
опрятность кода и очевидность принципов работы.
В итоге написал свою реализацию взяв за основу самую простую идею CAPTCHA
отсюда (спасибо авторам), кроме того оттуда же взят
набор изображений используемых для проверки.
Реализация представляет собой Perl-класс содержащийся в модуле, что значительно
упрощает его использование в уже существующем коде (см. пример).
Кроме того выполнен ряд улучшений: например код ориентирован на выполнение в
любом окружении (как CGI, так и mod_perl) - все необходимые для работы данные
передаются в код при инициализации объекта класса NPCaptcha Фактически
код переписан с нуля.
Принцип
Выбранная мною реализация базируется на принципе определения IP адреса
пользователя и отдачи браузеру уже готовых изображений хранящихся в каталоге
заданном при создании объекта класса. Имена файлов изображений без расширения
соответствуют тому что на них отображено. Например изображение с именем файла
JWBV.gif будет содержать буквы JWBV. Имея IP адрес удалённого пользователя и
значение CAPTCHA полученное из имени файла, скрипт сохраняет локально
информацию о том какое значение было отправленно данному IP адресу. В случае
получения ответа от удалённого пользователя содержащего значение CAPTCHA скрипт
проверяет создана-ли запись для этого IP адреса, если создана - проверка
пройдена.
Хоть принцип очень прост, но как показывает практика - достаточно надёжен, даже
учитывая вероятность работы нескольких пользователей из под одного IP адреса.
Во всяком случае для небольших проектов. С момента установки CAPTCHA в моей
гостевой не появилось ни одной новой записи от бота :-).
Алгоритм
Алгоритм работы данного варианта CAPTCHA состоит из двух частей. Первая часть -
выдача пользователю случайной картинки:
Скрипт считывает имена всех файлов изображений использующихся для проверки из
каталога заданного при его старте.
Выбирается случайный файл из полученного списка и во временной директории
создаётся пустой временный файл с именем (IP-адрес-пользователя)-(имя-картинки-без-расширения).captcha.
Файл считывается и отдаётся в браузер пользователя.
Кроме того в браузер пользователя отдаётся cookie с значением равным его IP
адресу.
Вторая часть алгоритма скрипта запускается после того как пользователь
распознаёт значение на рисунке и отправляет форму с данными на сервер.
Скрипт получает значение введённое пользователем и его адрес, после чего
генерирует имя файла: (IP-адрес-пользователя)-(значение-введённое-пользователем).captcha
Проверяет существует-ли файл с таким именем во временном каталоге, в который
ранее была произведена запись.
Если существует - удаляет этот файл и возвращает в вызвавшую программу значение
"истина".
Если не существует - проверяет наличие файла (значение-из-cookie)-(значение-введённое-пользователем).captcha,
если существует - удаляет файл и возвращает в вызвавшую программу значение
"истина".
Иначе - возвращает значение "ложно".
Кроме того, каждый раз при запуске на выполнение код проверяет наличие временных
файлов возраст которых превысил заданное значение таймаута, если таковые
обнаруживаются - они автоматически удаляются.
Методы и параметры
Ниже описаны методы Perl-класса и параметры принимаемые конструктором при
инициализации объекта.
NPCaptcha::new - конструктор класса, должен быть обязательно вызван
перед использованием объекта. Хеш параметров использует следующие ключи: imagesdir - каталог содержащий файлы изображений CAPTCHA. По умолчанию -
'./images' tmpdir - каталог для записи временных файлов. По умолчанию - '/tmp' timeout - задаёт время (в секундах) спустя которое временные файлы
считаются устаревшими для проверки и удаляются. По умолчанию - 600 секунд (10
минут). extensions - список расширений файлов с изображениями. По умолчанию -
gif remoteAddr - задаёт IP удалённого пользователя. Определяется либо через
функции mod_perl, либо через переменную $ENV{REMOTE_ADDR}. Не
имеет значения по умолчанию. cookieName - имя cookie использующейся для отправки IP адреса в браузер
пользователя. По умолчанию - 'captcha_rcpt'. cookieValue - значение cookie полученное каким-либо образом (например
через $ENV{HTTP_COOKIE} или fetch CGI::Cookie). captchaValue - собственно значение CAPTCHA введённое пользователем в
WEB-форму.
NPCaptcha::clearOld
- удаление устаревших временных файлов. Вызывается автоматически из
конструктора.
NPCaptcha::getImage
- метод возвращающий считанное из файла изображение. Полученное значение можно
выдать в браузер любым стандартным для данного случая методом. Например print
$obj->getImage() в CGI или $r->print($obj->getImage()) в mod_perl.
Не имеет параметров.
NPCaptcha::getCookie - возвращает в вызвавший скрипт строку вида: captcha_rcpt=(IP-адрес-пользователя); expires=Sun Jan 31 02:21:59 2010;
path=/
Должна быть вызвана до первого вывода HTML кода в скрипте, потому что эту
строку нужно передать в браузер пользователя как cookie. Не имеет параметров.
NPCaptcha::check - функция выполняющая провеку введённого пользователем
значения. Возвращает истину если проверка прошла успешно, иначе - ложь. Не
имеет параметров.
Пример использования
Модуль NPCaptcha.pm должен
находиться в одном из стандартных каталогов Perl-модулей, либо в коде скрипта
должна использоваться директива: use lib qw(/путь/к/каталогу/с/модулем);
Ниже приведён простейший пример использования CAPTCHA для защиты формы от
автозаполнения ботами. Состоит из двух частей, первая часть - скрипт отдающий
изображения в браузер пользователя:
Вторая часть - скрипт создающий форму защищённую от автозаполнения:
То как это выглядит и работает на практике:
Если у Вас возникли какие-либо замечания/предложения/пожелания по поводу данной статьи,
то - предлагаю их сообщить по , либо высказать в гостевой.