Капча на PHP

В современной веб-форме мы часто сталкиваемся с изображением captcha. Это необходимое средство защиты от автоматического заполнения данными. Эта статья научит вас как сделать самому Captcha (капча) для веб-формы. После прочтения этой статьи, вы самостоятельно сможете создать капчу для своих проектов.

Разработка CAPTCHA

Я не буду объяснять что такое капча, предполагаю, что у Вас уже есть общие знания об этой технологии. Эта статья предназначена только объяснить и показать, как это действительно работает.

Капча на PHP

к меню ↑

Рисование каптчи

Для того что бы нарисовать капчу нужно установить библиотеку GD (Graphics Draw). Эта библиотека рисует графику и изображение с помощью встроенных функции в PHP.

к меню ↑

Установка GD

Для установки GD на Ubuntu запустите sudo apt-get install php5-gd.

В Windows, вы должны включить модуль GD2 DLL php_gd2.dll в php.ini. Старый модуль GD1 DLL php_gd.dll был удален в версии PHP 4.3.2. Функции для работы с полноцветными изображениями, такие как imagecreatetruecolor(), требуют наличие GD2.

Повысить возможность библиотеки GD для работы с большим количеством форматов изображений можно используя опцию —with-XXXX во время конфигурации PHP.

Поддерживаемые форматы изображений
Формат изображения Опции при конфигурации
jpeg Чтобы включить поддержку jpeg добавьте —with-jpeg-dir=DIR. Jpeg 6b, 7 или 8 поддерживаются.
png Чтобы включить поддержку png добавьте —with-png-dir=DIR. Внимание! libpng требует наличие библиотеки zlib, поэтому добавьте —with-zlib-dir[=DIR] при конфигурации.
xpm Чтобы включить поддержку xpm добавьте —with-xpm-dir=DIR . Если во время конфигурации не удается найти необходимую библиотеку, можно указать путь к библиотеке X11.
к меню ↑

Создание капчи на PHP

Капча, как правило, состоит из 3-х составляющих : фигура, искажение и текст.

Мы будем выполнять описанные ниже действия:

  • Вывести пустое изображение в браузер
  • Создать фигуру для капчи
  • Генерирование случайных линий
  • Генерирование случайных точек
  • С генерировать случайный текст
к меню ↑

Вывести пустое изображение

С помощью простого тега “img” капча будет отображать внешнее изображение. Для этого потребуется две функции; одна для создания изображения, а другая для вывода его в браузер.

Создать и вывести изображение капчи:

<?php
session_start();
?>

    <title>demo.php</title>
    <body style="background-color:#ddd; ">

    <?php
    create_image();
    display();
    /***** определение функций *****/
    function display()
    {
        ?>

        <div style="text-align:center;">
            <h3>Введите текст, который видите на картинке</h3>
            <b>Чтобы проверить, что вы не робот</b>

            <div style="display:block;margin-bottom:20px;margin-top:20px;">
                <img src="image.png">
            </div> //закрыт div1
        </div> //закрыт div2

    <?php
    }

    function  create_image()
    {
        $image = imagecreatetruecolor(200, 50);
        imagepng($image, "image.png");
    }

    ?>
    </body>
<?php
?>

Первая строка проверяет, открыта страница с капчей.

В функции display() не что иное, как обычный код HTML, который выводит изображение в браузер.

В функции create_image() переменная image определяет изображение возвращенного функцией imagecreatetruecolor(), которая в качестве своих аргументов принимает длину и ширину изображения. Функция imagepng() создает изображение png по указанному пути и имени (в этом же каталоге).

Если вы сделали всё правильно, изображение капчи на странице будет в виде черного прямоугольника шириной 200px и высотой 50px.

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

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

к меню ↑

Фигура для капчи

Фигура для капчи может быть любая, например, эллипс, круг и другие. В нашем примере Капча на PHP прямоугольник с шириной 200 и высотой 50px. Функция imagecolorallocate()принимает цвет переменной где цвет RGB в качестве аргументов. Цвет фона заливки прямоугольника значение функции imagefilledrectangle().

Следующий код добавляться в функцию create().

$background_color = imagecolorallocate($image, 255, 255, 255);
imagefilledrectangle($image,0,0,200,50,$background_color);

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

к меню ↑

Генерировать случайные линии

В нашем примере Капча на PHP линии нужны что бы исказить изображение капчи для нечеткого чтения. В PHP линии создаются по координатам от начальной точки (x1,y1) до конечной точки (x2,y2). Теперь, как сделать чтобы наши линии были по диагонали поля капчи, берём координаты  <x1,x2> для ширины нашего прямоугольника <0,200> где координаты <y1,y2> генерируется произвольно. Это позволит создать линии под разным углом. Мы будем генерировать всего несколько линий, поместив данный функционал внутри цикла for.

$line_color = imagecolorallocate($image, 64,64,64);
for($i=0;$i<10;$i++) {
    imageline($image,0,rand()%50,200,rand()%50,$line_color);
}

Функция imageline()принимает координаты x1,x2,y1,y2 в качестве аргументов. В предыдущем шаге цвет был выделен только в качестве заливки фона.

y — координаты в виде rand()%50 потому что это высота нашего поля для капчи и всегда будет возвращать значение ниже 50. В качестве альтернативы вы можете использовать rand(0,50).

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

Случайные линии капчи
Вид прямоугольника капчи после добавления случайных линии
к меню ↑

Генерировать случайные точки

Случайные точки будут создаваться точно так же, как и произвольные линии используя функцию imagesetpixel(). Эта функция принимает значение координат где точки будут размещаться в поле капчи.

$pixel_color = imagecolorallocate($image, 0,0,255);
for($i=0;$i<1000;$i++) {
    imagesetpixel($image,rand()%200,rand()%50,$pixel_color);
}

x — координата генерируется случайным образом с помощью rand()%200 — это ширина прямоугольника капчи на PHP. В качестве альтернативы можно использовать rand(0,200).

Капча с случайными точеками
Вид прямоугольника капчи после добавления случайных точек
к меню ↑

Генерировать произвольный текст

Переменная $letters содержит буквы с верхним и нижним регистре.

$letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$len = strlen($letters);
$letter = $letters[rand(0, $len-1)];

$text_color = imagecolorallocate($image, 0,0,0);

Внутри цикла это выглядит так:

for ($i = 0; $i< 6;$i++) {
    $letter = $letters[rand(0, $len-1)];
    imagestring($image, 5,  5+($i*30), 20, $letter, $text_color);
    $word.=$letter;
}
$_SESSION['captcha_string'] = $word;

в произвольных линиях:

$word.=$letter;
 $_SESSION['captcha_string'] = $word;

смотрите в разделе, обработчик капчи.

Функция imagestring() напишет текст на изображении. Функция имеет 6 аргументов:

  1. Изображение.
  2. Размер шрифта текста (не более 5 -ти).
  3. x — координата (изменение пропорционально для каждой буквы).
  4. y — координата (так же, хотя можно изменить на произвольно).
  5. Фактическая строка, которая будет записана.
  6. Цвет шрифта текста.

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

Вычисление x — координаты осуществляется методом (научного тыка). Грубо говоря, буквы, расположенные на расстоянии около 35px (5+($i*30)) где $i=0,1,2,3,4,5,6. Если указать значение примерно 15-20px будут две пересекающиеся буквы. Если указать значение больше чем 40px, буквы вообще не поместятся в прямоугольнике капчи.

Код капчи создаст текст из 6 букв, их всегда можно добавить путём изменения аспектов координат y цвета и т.д.

Окончательный вид captcha:

Текст в captcha будет меняться при каждом обновлении страницы
Окончательный вид капчи с текстом

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

к меню ↑

Обработчик ответа капчи

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

Из предыдущих фрагментов кода остались две строки без объяснения:

  1. $word.=$letter; — оператор конкатенации используется для размещения букв по порядку создавая слово из 6-ти букв.
  2. $_SESSION['captcha_string'] = $word; сессия капчи используется для проверки достоверности ответа на капчу.
к меню ↑

Добавить капчу к форме

Добавлять форму нужно в определение функции display(). Для капчи будут использоваться две кнопки: одна для отправки submit , а другая обновить страницу если пользователь не понял буквы в капче.

Следующий код нужно добавить между закрытыми тегами div (см. комментарии для функции display() )

function display()
{
    ?>

    <div style="text-align:center;">
        <h3>Введите текст, который видите на картинке</h3>
        <b>Чтобы проверить, что вы не робот</b>

        <div style="display:block;margin-bottom:20px;margin-top:20px;">
            <img src="image<?php echo $_SESSION['count'] ?>.png">
        </div>
        <form action=" <?php echo $_SERVER['PHP_SELF']; ?>" method="POST"
        / >
        <input type="text" name="input"/>
        <input type="hidden" name="flag" value="1"/>
        <input type="submit" value="отправить" name="submit"/>
        </form>

        <form action=" <?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
            <input type="submit" value="обновить страницу">
        </form>
    </div>

<?php
}

Прежде чем продолжить, нам нужно знать когда показывать и когда не показывать поле ввода капчи.

Поле ввода капчи будет отображаться при условии:

  1. если страница просто загружена
  2. если ответ был неверным

Первое условие выполняется с помощью переменной $flag, и имеет значение ‘1’ каждый раз при нажатии кнопки submit. Второе условие достигается путем проверки совпадения значении вводимыми пользователем и хранящихся в нашей переменной (см. код ниже).

Чтобы добиться этого, мы заменим строки нашего кода:

create_image();
    display();

на:

$flag = 5;

if (isset($_POST["flag"])) // проверка, что переменная POST не является пустым
{
    $input = $_POST["input"];
    $flag = $_POST["flag"];
}

if ($flag == 1) // кнопка submit была нажата
{
    if (isset($_SESSION['captcha_string']) && $input == $_SESSION['captcha_string']) // ввод пользователя и строка ЗАЩИТНЫЙ КОД совпадает
    {

        ?>

        <div style="text-align:center;">
            <h1>Ваш ответ правильный!</h1>

            <form action=" <?php echo $_SERVER['PHP_SELF']; ?>" method="POST"> // обновить страницу
                <input type="submit" value="обновить страницу">
            </form>
        </div>

    <?php

    } else // ответ неправильный, ЗАЩИТНЫЙ КОД показан снова
    {

        ?>
        <div style="text-align:center;">
            <h1>Ваш ответ неправильный!<br>пожалуйста попробуйте снова</h1>
        </div>
        <?php
        create_image();
        display();
    }

} else // страница просто была загружена
{
    create_image();
    display();
}

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

Теперь капча на PHP будет выглядеть так:

Вид капчи на странице ввода
Вид капчи для ввода букв пользователем

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

Ввод капчи является неправильным
Неправильный ввод букв на изображении капчи

Если входные данные введены правильно пользователю будет показано сообщение.

Сообщение пользователю капчи
Входные данные капчи введены правильно

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

Чтобы браузер не находил изображения в кэше есть очень простое решение — создадим уникальные имена. Добавим функцию time() с именем изображения при создании для отображении в капче.

Добавить эту строку нужно там где начинали сессии:

$_SESSION['count']=time(); // уникальная строка

Заменить тег img src в функции display() на

<img src="image<?php echo $_SESSION['count']?>.png">

И та часть, где мы создали изображение png в функции create_image() также замените на:

imagepng($image,"image".$_SESSION['count'].".png");

Изображение теперь будет называться примерно так : image39342015.png. Эта процедура будет создавать изображение столько раз, сколько раз обновляется страница, это может занять огромное количество дискового пространства, поэтому мы убедимся что перед созданием изображения, все другие изображения с расширением png удаляются.

Добавьте следующею функцию вызова перед imagepng():

$images = glob("*.png");
foreach($images as $image_to_delete)
{
    unlink($image_to_delete);
}

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

Скачать captcha на PHP

к меню ↑

Заключение

В этой статье мы рассмотрели три основные вещи используемые для создания стандартной captcha — фигура, искажения и текст. Существует ReCaptcha, которая также поддерживает ввод звука, для людей с плохим зрением. Мы надеемся что вы нашли эту статью полезной.