Captcha
Опубликовано: 21 августа 2010 Обновлено: 12 октября 2016

CAPTCHA (от англ. Completely Automated Public Turing test to tell Computers and Humans Apart – полностью автоматизированный публичный тест Тьюринга для различия компьютеров и людей) – торговая марка Университета Карнеги-Меллона, в котором разработали компьютерный тест, используемый для того, чтобы определить, кем является пользователь системы: человеком или компьютером.

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

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

1. Давать математические задачки. Например, "17 + 26 - 15 = ", ответом будет: "28".

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

Одно время, такие капчи были весьма популярны в CMS типа WordPress, Drupal и т.п.

2. Задавать вопрос, на который существует единственно верный ответ. Например, вопрос: "Назовите столицу Зимбабве", ответом будет: "Хараре".

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

Вдобавок, сколько людей ответило бы на вопрос о столице Зимбабве без поиска в гугле?

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

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

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

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

Что нам потребуется для реализации? – желание, усердие и прямые руки :-)

Алгоритм генерации капчи у нас будет такой:
1. Случайным образом генерируем секретный код.
2. Сохраняем секретный код в сессию пользователя. Сессия пользователя хранится на стороне сервера и не доступна извне.
3. Создаем изображение с отображением секретного кода.
4. Применяем к изображению кода искажение, шумы, размытие, чтобы увеличить сложность машинного разбора. Тут главное не переборщить и сделать так, чтобы человек смог разобрать, что написано.
5. Выдаем изображение браузеру.

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

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

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

<?php /*! © darkslave :: http://darkslave.net/ */ // ширина капчи $imageWd = 240; // высота капчи $imageHg = 60; // файл шрифта $fontPath = "./consolas.ttf"; // размер шрифта $fontSize = 36; // количество символов в капче $codeLength = 6; // набор символов для генерации кода $codeCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // амплитуда размытия $blurDelta = 6; // частота размытия = 2π / период размытия в пикселах $blurFrequency = 0.2; // начальная фаза (задается случайно) $blurPhase = 0.017453292 * mt_rand(0, 360);

Переходим к генерации секретного кода. Код будем собирать из указанного набора символов $codeCharset:

$code = ""; $charsetLast = strlen($codeCharset) - 1; for ($i = 0; $i < $codeLength; $i++) { $code .= $codeCharset[ mt_rand(0, $charsetLast) ]; }

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

Например, можно поступить так:
1. Сохранять в базу связку идентификатор + секретный код.
2. Идентификатор передавать пользователю посредством cookie.
3. При верификации формы необходимо будет запрашивать секретный код по его идентификатору из cookie и сравнивать его с тем кодом, что указал пользователь.

session_start(); $_SESSION['captcha_code'] = $code;

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

$image = imagecreatetruecolor($imageWd, $imageHg); $color = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $color); imagecolortransparent($image, $color);

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

// определяем границы текста $bounds = imagettfbbox($fontSize, 0, $fontPath, $code); $startX = max(($imageWd - abs($bounds[2])) / 2, 0 ); $startY = min(($imageHg + abs($bounds[7])) / 2, $imageHg); // цвет текста $color = imagecolorallocate($image, 0, 0, 0); // отрисовываем текст imagettftext($image, $fontSize, 0, $startX, $startY, $color, $fontPath, $code);

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

// сохраняем оригинал $original = $image; // создаем изображение для капчи $image = imagecreatetruecolor($imageWd, $imageHg); $color = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $color); imagecolortransparent($image, $color); // применяем размытие for ($i = 0; $i < $imageHg; $i++) { $dx = $blurDelta * cos($blurFrequency * $i + $blurPhase); imagecopy($image, $original, $dx, $i, 0, $i, $imageWd, $imageHg - $i); }

Все, капча готова. Остается отдать ее браузеру.

header("Cache-Control: no-store, no-cache, must-revalidate"); header("Expires: " . date("r")); header("Content-Type: image/png"); imagepng($image);

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

Пример работы капчи можно увидеть ниже:

Исходный код из статьи можно забрать здесь: скачать исходники.

Добавить комментарий к статье:
― показать еще комментарии ―