Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > Много капч на одной странице.


Автор: ksnk 16.7.2013, 11:25
Внезапно потребовалось на одной страничке разместить несколько несвязанных друг с другом форм, каждая из которых защищена капчей.
Капча формируется одним и тем-же скриптом, который просто складывает в сессию текущее сгенерированное значение и при проверке ввода юзера тупо сравнивается введённые цифры с сессионными.

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

Что хочется:
Чтобы у каждой из множества форм на странице была своя картинка капчи.
Чтобы имелась возможность несколько раз открыть форму логина и заполнить ЛЮБУЮ из открытых форм. Пример - форма логина расположена на каждой странице сайта, юзер понаоткрывал несколько вкладок и внезапно решил на какой-то залогиниться. 

Желательно, чтобы при обновлении страницы капча не обновлялась, а только по кнопке "обновить капчу". Просто для экономии рессурсов сервера.

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

пока видится такое решение:
  •  при генерации формы с капчей генерируется уникальное многобуквенное значение YYY, которое передается скрытым полем в форму.
  •  запрос капчи идет по адресу /images/captcha_YYY.png. В случае, если картинки нет - перенаправляем htaccess'ом на капче-построитель, который построит нужную нам картинку.
  •  В сессии при построении картинок формируется массив из значений YYY=>`слово, которое написано на капче`, слово случайно генерируется построителем.
  •  При проверке формы, по значению YYY получаем нужное слово и сравниваем его с вводом пользователя.

Дополнительные проблемы. Ненужные картинки капчи требуется периодически удалять. По идее, время жизни картинок не более времени жизни сессии, 10-20 минут, что значительно упрощает логику этого процесса.

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

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

Автор: MoLeX 16.7.2013, 12:01
Цитата(ksnk @  16.7.2013,  11:25 Найти цитируемый пост)
при генерации формы с капчей генерируется уникальное многобуквенное значение YYY, которое передается скрытым полем в форму.

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

Автор: Arantir 16.7.2013, 12:26
Что-то не совсем понял, как это без сессий получится.
Если нет сессии, то между двумя запросами нет связи. Каждое обновление, как новый посетитель. А вы говорите:
Цитата(ksnk @  16.7.2013,  10:25 Найти цитируемый пост)
Желательно, чтобы при обновлении страницы капча не обновлялась, а только по кнопке "обновить капчу". Просто для экономии рессурсов сервера.


Цитата(ksnk @  16.7.2013,  10:25 Найти цитируемый пост)
Хранить информацию о запрошенных капчах и словах разумно в базе.

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


Ну, в целом, вариант "токен  => слово" с передачей токена в форму — самый непосредственный (без заморочек). 

Автор: ksnk 16.7.2013, 13:46
Цитата(Arantir @  16.7.2013,  12:26 Найти цитируемый пост)
Если нет сессии, то между двумя запросами нет связи. Каждое обновление, как новый посетитель.

В общем, да, без сессии не получается. 
Решением, вероятно, будет хранение в сессии значений YYY с привязкой к имени формы (уникально на странице и, при необходимости, по всему сайту). При попытке ввода капчи с таким YYY будем его сбрасывать, чтобы перебор на разных вкладках не срабатывал. Так получится, что для одинаковых форм будет выводится одна и та же капча, при обновлении, выводится она же, при попытке неправильного ввода, при обновлении вкладок выведется другая... Вроде пока получается. 
Вечером, возможно, доберусь и до  кода. smile

Автор: ksnk 18.7.2013, 07:48
Добрался до кода. При ближайшем рассмотрении моих привычек и имеющихся проектов, оказалось, что каждая форма УЖЕ имеет свой уникальный идентификатор - имя. Как правило, форма генерируется плагином с уникальным иенем, а внутри плагина все формы перечтены, и имена у них уже есть. Так что придумывать еще одно уникальное значение не нужно, да и сохранять его в скрытом поле не всегда необходимо, только чтобы отличать одну форму от другой, если больше никак...

Таким образом, решение сейчас выглядит так - класс $captcha=new captcha(`имя формы`).
  • reset() - сброс текущей капчи для формы с этим именем.
  • clearOldFiles() - очистка всех старых картинок капч.
  • fileName() - выдать имя файла картинки с капчей.
  • is_correct(`значение пользователя`) - сравнить введенную капчу с сохраненным значением.
  • createImage() - выдать на улицу сгенерированную картинку. C нужными заголовками.
При генерации формы с капчей нужно запросить имя файла и вмонтировать его в картинку. Имя файла выглядит как `captcha_XXX_YYY.png`, где XXX - crc32 от комбинации имени формы и ID сессии, YYY - time() от времени генерации записи о картинке. Пример - .../test.captcha/captcha_3393001107_1374108457.png

При проверке введенных данных, вызывается is_correct, при этом сама капча безусловно сбрасывается.

При запросе картинки, если в сессии нет записи с индексом XXX, выводим 404, значит откуда  то всплыли старые ссылки. Если нет картинки с нужным адресом - генерируем ее и read_file'ом выдаем юзеру. Остальные ссылки на эте картинку будут либо перехватываться htaccess'ом, либо будем отдавать уже готовый файл.

Код, в принципе, готов. Нужно только причесать и воткнуть в живой проект.

Автор: Fortop 18.7.2013, 11:28
ksnk, а чем тебе мешала одинаковая капча на всех формах?

Автор: ksnk 18.7.2013, 11:56
Соображения, больше, эстетскго плана.

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

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

То есть, более правильно, imho, привязывать капчу к форме, а не к сайту.

Автор: Fortop 18.7.2013, 12:15
Цитата(ksnk @  18.7.2013,  11:56 Найти цитируемый пост)
 и на второй странице у него сразу оказалась невалидная капча.

типичный случай на большинстве сайтов


Цитата(ksnk @  18.7.2013,  11:56 Найти цитируемый пост)
. А вот с разными формами, 

Не вижу принципиальной разницы. Чем эта же форма открытая во второй вкладке отличается от другой формы, открытой в 3й вкладке?

Цитата(ksnk @  18.7.2013,  11:56 Найти цитируемый пост)
привязывать капчу к форме, а не к сайту.

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

Автор: ksnk 18.7.2013, 14:13
Цитата(Fortop @  18.7.2013,  12:15 Найти цитируемый пост)
Не вижу принципиальной разницы. Чем эта же форма открытая во второй вкладке отличается от другой формы, открытой в 3-й вкладке?

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

Изначально возникло 2 желания.
- Иметь разные картинки капчи на разных формах на одной странице.
- не менять капча-картинки при обновлении страницы.

Осмысленность этих желаний - обсуждается. smile

Автор: Fortop 18.7.2013, 16:13
Цитата(ksnk @  18.7.2013,  14:13 Найти цитируемый пост)
форму можно заполнять только один раз

добавить товар? smile

Автор: ksnk 18.7.2013, 16:51
Цитата(Fortop @  18.7.2013,  16:13 Найти цитируемый пост)
добавить товар?

Добавить товар - без капчи везде, так что не защитывается.  smile 

Автор: Fortop 18.7.2013, 17:11
ок, провести платеж

Автор: ksnk 18.7.2013, 17:43
Ну, это убедительнее, но есть и возражения.

Где проводим и кто? В админке нету капчи, так что про что это? юзер с сайта оформляет заказ на накликанный в корзинку товар? При этом открыл корзинку в нескольких вкладках? imho, очень разумно будет не позволить ему оформить заказ и не позволить оплатить его еще раз с соседней вкладки.

Автор: Fortop 18.7.2013, 21:24
Да не, просто комунальный платеж
Свет. вода, газ.

Или оплата поставщику 1,2,3.
Не из корзины

Добавлено через 13 секунд
а вообще о чем мы сейчас рассуждаем? smile

Автор: ksnk 19.7.2013, 00:53
Цитата(Fortop @  18.7.2013,  21:24 Найти цитируемый пост)
Да не, просто комунальный платеж
Свет. вода, газ.

Это действие тоже как бы не случайно. Юзер заходит в банковский клиент и там уже выбирает нужные счет для оплаты. Чем не админка? Без регистрации такое никому позволить нельзя, так что форма регистрации уже была, капча не  нужна.

Или есть какой реальный пример?

Цитата(Fortop @  18.7.2013,  21:24 Найти цитируемый пост)
а вообще о чем мы сейчас рассуждаем?  

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

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

Автор: Arantir 19.7.2013, 02:29
Почему именно каптча? Защита от роботов? Страница с кучей форм на всеобщем обозрении?

Есть такая штука - токен. Используется, в частности, для предотвращения http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D0%BB%D0%BA%D0%B0_%D0%BC%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D1%85_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2.
Делает 3 вещи: гарантирует, что форма отправлена с предыдущим ее получением с сайта (токен генерируется при генерации страницы); что форма отправлена в той же сессии (токен хранится в сессии); что эта же форма не будет обработана второй раз (при получении токена он аннулируется).
В частности, как следствие, при открытии новой вкладки старая вкладка делается невалидной.

Оной способ можно модифицировать:
Генерировать токен только при отсутствии его в сессии.
Хранить токены по именам полей в форме.
Время жизни токена добавить по вкусу.

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


А для защиты от роботов на всю страницу всучить одну единственную каптчу где-то вверху/внизу и все...

Автор: ksnk 19.7.2013, 07:42
Цитата(Arantir @  19.7.2013,  02:29 Найти цитируемый пост)
Почему именно каптча?

Потому, что в техзадании написано - для нерегистрированного юзера, формы защищаются капчей. Не обсуждается и не лечится smile 
Цитата(Arantir @  19.7.2013,  02:29 Найти цитируемый пост)
 Страница с кучей форм на всеобщем обозрении?

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

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

Про токен - в теперешней реализации - оказывается он есть у меня.  smile При наличии в сессии фактов: 1- запроса страницы с формой (токен появляется) 2- запроса картинки броузером (слово появляется) форма может проверятся. Если исключить факт ручного ввода юзером капчи - получится токен, практически в чистом виде, разве что привязки немного другие. Надо будет подумать, куда можно пристроить.

Ежели про теорию.
Нормальное, imho, устройство защиты сайта капчей - это капча, появляющаяся раз в 5 минут при очередной отсылке формы с сайта. Формально, каждая форма имеет капчу. Просто при проверке первой формы устанавливается в сессии флажёк - `капчу не проверяем 5 минут`, выставляем куку и с каждой формы сайта по куке скрываем поле с капчей. По истечении времени - показываем (в куке написано время). 

Автор: ksnk 25.7.2013, 02:27
Воткнул в рабочий проект, полет пока нормальный.

https://github.com/Ksnk/captcha/blob/master/captcha.php

https://github.com/Ksnk/captcha/blob/master/sample/sample1.php

https://github.com/Ksnk/captcha.

Из принципиальных плюсов - картинка не перерисовывается при перерисовке страницы. 

Из тонких моментов 

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

Автор: Sanchezzz 25.7.2013, 12:28
Хотел посмотреть на гите а там фейк)  smile 

Автор: ksnk 25.7.2013, 14:38
Блин! При входе на гит мне тамошний робот говорит про 
Цитата

Our trusty robot, Hubot, seems to think you are not a human.
Because of that, he's hidden your profile from the public. If you really are human, please contact support to have your profile reinstated.
We promise we won't require DNA proof of your humanity.

Че это он?

Если я захожу с компьютера, с которого не ходил в гугл, то тоже показывает 404 страницу. :( Вступил в переписку с роботом, может что умное расскажет.

Вот, пример использования, раз уж такая пьянка
Код

<?php
/**
 * sample of CAPTCHA usage
 */

// for debugging. Comment this if you really sure
error_reporting(E_ALL);
ini_set('display_errors', 1);

session_start();
/**
 * place pictures just here. no mater the cost
 */
$_SESSION['captcha_setup'] = array(
    'PICTURE_PATH' => __DIR__ . '/',
    'PICTURE_URI' => '/projects/captcha/sample/',
);

include("../captcha.php");

if ($_SERVER['REQUEST_METHOD'] == "POST") {
    if (empty($_POST['handle'])) exit(555);
    $_captcha = new captcha($_POST['handle']);
    if (isset($_POST['reload_captcha_x'])
        || isset($_POST['reload_captcha'])
    ) {
        $_captcha->reset();
    } elseif ($_captcha->is_correct($_POST['captcha'])) {
        echo 'Captcha is correct<br>';
    } else {
        echo 'Incorrect captcha<br>';
    }
}
?>
<style>
    .cimage {
        width: 100px;
        height: 48px;
    }
</style>
<h3>form1</h3>

<?php $_captcha = new captcha('form1'); ?>
<form method="POST" action="">
    <input type="hidden" name="handle" value="form1">
    <label>
        Type the digits
        <input type="text" name="captcha">
    </label>
    <img class="cimage" src="<?= $_captcha->pictureName(true) ?>">
    <input type="submit" value="reload" name="reload_captcha">
    <input type="submit">
</form>

<h3>form2</h3>

<?php $_captcha = new captcha('form2'); ?>
<form method="POST" action="">
    <input type="hidden" name="handle" value="form2">
    <label>
        Type the digits
        <input type="text" name="captcha">
    </label>
    <input type="image" class="cimage" src="<?= $_captcha->pictureName(true) ?>"
           title="Click to reload captcha" name="reload_captcha">
    <input type="submit">
</form>


http://ksnk.500mb.net/captcha/sample/sample1.php

Автор: Sanchezzz 25.7.2013, 17:08
Я первый  раз вижу что бы акк блокировали
Ты наверное быстро комиты отправляешь.

Так да, вроде хорошо получилось

Автор: ksnk 27.7.2013, 12:51
Разблокировали гитхаб. Не прошло и 3х дней. Что было - не объясняют, сорри, типа, ошибочка вышла. Ну да и ладно.

Можно смотреть https://github.com/Ksnk/captcha, если интересно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)