В продолжение «недели капчи» на Хабре (см. топики 1, 2, 3) хочу коротко рассказать о своем варианте создания капчи с использованием сервисов поиска картинок.
Поиск картинок является ярким примером сложнообратимой операции. То есть восстановить поисковый запрос, имея результат в виде набора картинок, не всегда легко. Но очевидно, что человеку это сделать гораздо проще, чем компьютеру. Особенно если имеется достаточное количество изображений.
Крупные сервисы поиска картинок (например google или yandex) хорошо решают прямую задачу, находя картинки по заданному ключевому слову. Сделав такой запрос на стороне сервера, мы получаем набор картинок и можем предложить пользователю решить обратную задачу — определить исходное ключевое слово. Это и будет подтверждением, что он не робот.
Но даже для человека не всегда просто восстановить слово по результатам поисковиков. Поэтому дополнительной подсказкой будет служить само исходное слово, в котором часть символов заменены на звездочки. В этом случае задача решается гораздо проще.
Первое, что нужно сделать — это сформировать список слов, которые будут использоваться для капчи. Слова должны быть общеизвестными существительными, а также не слишком короткими и не слишком длинными. В качестве базы логично использовать частотный словарь, отфильтрованный по словам с длинной от 5 до 10 символов. Разумеется, в реальных условиях данные об используемом словаре не должны разглашаться.
Извлекаем слово случайным образом:
Далее формируем запрос к сервису поиска картинок (google) и получаем результат. Запрос отправляем из PHP с помощью библиотеки curl:
Для извлечения картинок из HTML-ответа используем регулярное выражение:
Формируем подсказку — заменяем 3 символа в слове на звездочки:
И наконец выводим пользователю слово со звездочками, набор картинок и поле для ввода:

Рабочий пример полученной капчи можно посмотреть тут.
Там же видна статистика верных/неверных ответов, которая покажет «профпригодность» капчи.
Конечно, есть слабые места:
Идея
Поиск картинок является ярким примером сложнообратимой операции. То есть восстановить поисковый запрос, имея результат в виде набора картинок, не всегда легко. Но очевидно, что человеку это сделать гораздо проще, чем компьютеру. Особенно если имеется достаточное количество изображений.
Крупные сервисы поиска картинок (например google или yandex) хорошо решают прямую задачу, находя картинки по заданному ключевому слову. Сделав такой запрос на стороне сервера, мы получаем набор картинок и можем предложить пользователю решить обратную задачу — определить исходное ключевое слово. Это и будет подтверждением, что он не робот.
Но даже для человека не всегда просто восстановить слово по результатам поисковиков. Поэтому дополнительной подсказкой будет служить само исходное слово, в котором часть символов заменены на звездочки. В этом случае задача решается гораздо проще.
Реализация
Первое, что нужно сделать — это сформировать список слов, которые будут использоваться для капчи. Слова должны быть общеизвестными существительными, а также не слишком короткими и не слишком длинными. В качестве базы логично использовать частотный словарь, отфильтрованный по словам с длинной от 5 до 10 символов. Разумеется, в реальных условиях данные об используемом словаре не должны разглашаться.
Извлекаем слово случайным образом:
$words = file('words.txt');
$index = rand(0, count($words) - 1);
$q = trim($words[$index]);Далее формируем запрос к сервису поиска картинок (google) и получаем результат. Запрос отправляем из PHP с помощью библиотеки curl:
$url = 'http://www.google.com/search?tbm=isch&hl=ru&source=hp&biw=1024&bih=587&q='.$q.'&gbv=2&aq=f&aqi=g10&aql=&gs_sm=s';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);Для извлечения картинок из HTML-ответа используем регулярное выражение:
$regexp = '/<img[^>]*src="(http\:\/\/t\d\.gstatic\.com\/images[^"]+)"[^>]*>/is';
preg_match_all($regexp, $result, $matches);
Формируем подсказку — заменяем 3 символа в слове на звездочки:
$show_word = '';
$l = mb_strlen($q);
$star_len = 3;
$start = ceil(($l - $star_len)/2)-1;
$end = $l - floor(($l - $star_len)/2);
for($i=0; $i<$l; $i++) {
if($i<=$start || $i>=$end) {
$show_word .= mb_substr($q, $i, 1).' ';
} else {
$show_word .= '* ';
}
}И наконец выводим пользователю слово со звездочками, набор картинок и поле для ввода:

Тест и критика
Рабочий пример полученной капчи можно посмотреть тут.
Там же видна статистика верных/неверных ответов, которая покажет «профпригодность» капчи.
Конечно, есть слабые места:
- иногда невозможно определить слово
- зависимость от других сервисов
- занимает много места на странице