О создание капчи на PHP+Jquery без задействования графических изображений.
Сегодня Интернет просто изобилует бесконечным количеством инструкций, в которых различные авторы, рассказывают о своём собственном способе организации защиты от ботов, жаль только, что большинство этих авторов на языке носят типун. Вот, к примеру, плагин jQuery под названием Real Person, он создаёт на странице, нечто подобное:
Причём, стоит обратить внимание на то, что все буквы созданы при помощи всего лишь одного символа — астериска, без использования каких либо изображений. На сайте автора есть примеры, которые показывают, как легко можно изменить длину и набор символов для генерации защитного кода. Также там вы найдёте пример серверного скрипта для проверки правильности ввода символов:
Ё-моё, и вот он Epic Fail! Невиданная досада, автор предлагает нам полностью доверять данным, пришедшим от пользователя. Такая проверка надёжна… он хоть верит в это сам?! С учётом того, что исходный код функции «rpHash()», также описан на сайте автора этого плагина, то с чистой совестью можно было писать и такую проверку:
На мой взгляд, две строчки кода, описанные выше, полностью идентичны по уровню сложности обхода злоумышленниками. Но всё-таки Я считаю, что такой вид теста Тьюринга достаточно перспективен и поэтому Я решил описать своё видение организации такой защиты.
Данная CAPTCHA представляет собой, набор букв латинского алфавита и арабских цифр. Каждый символ представляет собой матрицу размером 7х7. Любая ячейка матрицы может быть занята или свободна. Занятая ячейка, по умолчанию, имеет тёмный фон, а свободная – прозрачный.
Пример буквы «M».
Любую ячейку можно однозначно определить с помощью двух координат – x и y. Чтобы в целостности воссоздать символ достаточно знать координаты только одного вида ячеек. Разумно хранить координаты занятых ячеек, так как их количество в несколько раз меньше свободных. Для хранения этой информации используется массив:
Array(x1, y1, x2, y2…,xN, yN)
Исходя из вышеизложенного, вот так выглядит буква «M»:
При генерации строки символов, надо учитывать смещение координаты x, каждого последующего символа, на n*7 ячеек, где n – количество символов–предшественников:
Соответственно, данную строку можно инициализировать как массив, получившийся в результате слияния двух массивов m(x, y) и a(x+7, y).
Сам генератор массива координат строк выглядит так:
PHP
Визуализируются символы, при помощи тега с заданным выравниванием по левому краю — float:left и чётко заданными размерами длины и высоты. В принципе, для данной цели подойдёт любой блочный элемент, но является одним из самых коротких доступных тегов, поэтому выбор пал на него. Сам код вывода:
JavaScript (jQuery)
Примечание. Сложность данного алгоритма оценивается как O(n*n), из-за вложенного цикла. Его можно усовершенствовать, добавив конструкцию break во второй цикл, которая будет вызываться при успешном выполнение условия, тогда во втором цикле будет просматриваться только часть массива, а не весь массив, как сейчас. Так же можно вынести внутренний цикл наружу, а в первом присваивать все тегам уникальный id, по которому второй цикл их легко распознает. Хотя это и приведёт к небольшому увеличению кода, но скорость работы заметно увеличиться.
В результате получаются вот такие строки:
1. Достаточно перехватить массив с координатами при загрузке его js`ом и сравнить его с шаблонами, чтобы расшифровать строку. Если же перед тем как функция get() вернёт этот массив, в нём перемешать пары, то это усложнит в n раз использование шаблонов.
Поэтому, перед тем как вернуть массив координат, в классе вызывается метод shuffle2, который аккуратно перемешивает массив, не путая пары x и y.
PHP
2. Если добавить в массив координат случайный шум, то это практически сделает невозможным использование шаблонов.
Шум будет двух видов, шум который забивает занятые ячейки, тем самым делая их свободными и шум, распространяющийся на фон, который некоторые свободные ячейки преобразует в занятые.
Для этого был немного модифицирован генератор массива координат строк:
PHP
В итоге:
Основной класс:
PHP
После создания phar и сжатия его, получается файл di_captcha.class.phar.gz весом 3.53кб.
Прример использования класса:
PHP+html
При запросе от js о генерации строки, возвращается количество символов и сам массив координат.
И, собственно, js скрипт:
JS
1. Если в класс генерации строк добавить шаблоны символов, по несколько шаблонов на символ, то это ещё больше усложнит расшифровку посредствам шаблонов.
2. Работники _http://decaptcher.com, _http://captchabot.com и _http://antigate.com/ скажут вам спасибо за такую капчу.
3. Эта статья была опубликована моим другом, в разделе веб разработка несколько дней назад, набрала 16 минусов, 14 плюсов и 11 коментов. Потом была похоронена модераторами по причине «не умеет читать правила сайта (публикует чужие посты и клянчит инвайты)», статья написана специально для сайта, и инвайты ни кто не клянчил, а уточнили, что он не автор статьи. Потом попала в песочницу и заслужила инвайт.
4. Краткий смысл 11 комментариев:
— чтобы обойти капчу надо перехватить post запрос и расшифровать текст по шаблонам
— с шумом при расшифровки будет большой брак
— с шумом буквы «О» и «D», «C» и «G» иногда трудно разборчивы и путаються, их лучше исключить из алфавита
— расшифровать можно любую капчу, главное найти компромисс между читаемостью и сложностью обхода
Скачать исходники
Демо: без шума, с шумом(1)
UPD: nagato разработал скрипт который удачно обходит капчу в 70% случаев.
Прелюдия
Сегодня Интернет просто изобилует бесконечным количеством инструкций, в которых различные авторы, рассказывают о своём собственном способе организации защиты от ботов, жаль только, что большинство этих авторов на языке носят типун. Вот, к примеру, плагин jQuery под названием Real Person, он создаёт на странице, нечто подобное:
Причём, стоит обратить внимание на то, что все буквы созданы при помощи всего лишь одного символа — астериска, без использования каких либо изображений. На сайте автора есть примеры, которые показывают, как легко можно изменить длину и набор символов для генерации защитного кода. Также там вы найдёте пример серверного скрипта для проверки правильности ввода символов:
if (rpHash($_POST['realPerson']) == $_POST['realPersonHash']) {
* This source code was highlighted with Source Code Highlighter.
Ё-моё, и вот он Epic Fail! Невиданная досада, автор предлагает нам полностью доверять данным, пришедшим от пользователя. Такая проверка надёжна… он хоть верит в это сам?! С учётом того, что исходный код функции «rpHash()», также описан на сайте автора этого плагина, то с чистой совестью можно было писать и такую проверку:
if ($_POST['In'])== $_POST['Out']) {
* This source code was highlighted with Source Code Highlighter.
На мой взгляд, две строчки кода, описанные выше, полностью идентичны по уровню сложности обхода злоумышленниками. Но всё-таки Я считаю, что такой вид теста Тьюринга достаточно перспективен и поэтому Я решил описать своё видение организации такой защиты.
Метод решения
Данная CAPTCHA представляет собой, набор букв латинского алфавита и арабских цифр. Каждый символ представляет собой матрицу размером 7х7. Любая ячейка матрицы может быть занята или свободна. Занятая ячейка, по умолчанию, имеет тёмный фон, а свободная – прозрачный.
Пример буквы «M».
Любую ячейку можно однозначно определить с помощью двух координат – x и y. Чтобы в целостности воссоздать символ достаточно знать координаты только одного вида ячеек. Разумно хранить координаты занятых ячеек, так как их количество в несколько раз меньше свободных. Для хранения этой информации используется массив:
Array(x1, y1, x2, y2…,xN, yN)
Исходя из вышеизложенного, вот так выглядит буква «M»:
$abc['m'] = array(1,1,7,1,1,2,2,2,6,2,7,2,1,3,3,3,5,3,7,3,1,4,4,4,7,4,1,5,7,5,1,6,7,6,1,7,7,7);
* This source code was highlighted with Source Code Highlighter.
При генерации строки символов, надо учитывать смещение координаты x, каждого последующего символа, на n*7 ячеек, где n – количество символов–предшественников:
Соответственно, данную строку можно инициализировать как массив, получившийся в результате слияния двух массивов m(x, y) и a(x+7, y).
Сам генератор массива координат строк выглядит так:
PHP
- // весь доступный алфавит
- $alphanum = 'abcdefghijkmnopqrstuvxyz0123456789';
- // цикл, генерации символов,
- // количество итераций цикла равно количеству символов в строке
- for ($i = 0; $i < $the_number_of_letters; ++$i) {
- // случайно выбирается символ
- $letter = $alphanum[intval(mt_rand(0, 33))];
- // создаётся массив символов $array_str
- foreach ($abc[$letter] as $key=>$val)
- // расставляются «правильные» координаты в массиве
- array_push($array_str, ($key%2 == 0)?$val+($i*7):$val);
- // запоминается сама строка
- $di_captcha_str .= $letter;
- }
* This source code was highlighted with Source Code Highlighter.
Визуализируются символы, при помощи тега с заданным выравниванием по левому краю — float:left и чётко заданными размерами длины и высоты. В принципе, для данной цели подойдёт любой блочный элемент, но является одним из самых коротких доступных тегов, поэтому выбор пал на него. Сам код вывода:
JavaScript (jQuery)
- // длина блока с тегами
- // от этого значения зависит количество тегов в каждой строке
- // вычисляется по формуле количество_ячеек+размер_отступа_между_ячейками*7
- // +двойной_размер_ячейки – отступ между символами
- // и всё это умножается на количество символов n
- $('#DICaptchaPic').css('width', ((((cell_size+2)*6)+(3*cell_size)+1)*n));
- // переменная для хранения списка тегов
- var html_p_tag = '';
- // цикл обхода всего массива с ячейками
- for (i = 1; i <= 7*7*n; ++i) {
- // если ячейка кратна семи, значит она крайняя в символе
- // и поэтому после неё необходим отступ межсимвольный
- var style = (i%7 == 0)?'margin-right: '+2*cell_size+'px;':'';
- // если ячейка занята, то её фон чёрного цвета
- for (j = 0; j < data[1].length; j += 2) style +=(((i%(data[0]*7)==0)?(data[0]*7):i%(data[0]*7)) == data[1][j] && Math.ceil(i/(data[0]*7)) == data[1][j+1])?'background-color: #000;':'';
- // закрывается тег
- html_p_tag += '<p'+((style=='')?'':' style=\''+style+'\'')+'>'; }
* This source code was highlighted with Source Code Highlighter.
Примечание. Сложность данного алгоритма оценивается как O(n*n), из-за вложенного цикла. Его можно усовершенствовать, добавив конструкцию break во второй цикл, которая будет вызываться при успешном выполнение условия, тогда во втором цикле будет просматриваться только часть массива, а не весь массив, как сейчас. Так же можно вынести внутренний цикл наружу, а в первом присваивать все тегам уникальный id, по которому второй цикл их легко распознает. Хотя это и приведёт к небольшому увеличению кода, но скорость работы заметно увеличиться.
В результате получаются вот такие строки:
Критика и доработка
1. Достаточно перехватить массив с координатами при загрузке его js`ом и сравнить его с шаблонами, чтобы расшифровать строку. Если же перед тем как функция get() вернёт этот массив, в нём перемешать пары, то это усложнит в n раз использование шаблонов.
Поэтому, перед тем как вернуть массив координат, в классе вызывается метод shuffle2, который аккуратно перемешивает массив, не путая пары x и y.
PHP
- function shuffle2($array) {
- for ($i = 0; $i < count($array); $i += 2)
- for ($j = count($array)-2; $j > $i; $j -= 2)
- if (mt_rand(0, 1) > 0) {
- $array[$i]+=$array[$j]; $array[$j]=$array[$i]-$array[$j]; $array[$i]-=$array[$j];
- $array[$i+1]+=$array[$j+1]; $array[$j+1]=$array[$i+1]-$array[$j+1]; $array[$i+1]-=$array[$j+1];
- }
- return $array;
- }
* This source code was highlighted with Source Code Highlighter.
2. Если добавить в массив координат случайный шум, то это практически сделает невозможным использование шаблонов.
Шум будет двух видов, шум который забивает занятые ячейки, тем самым делая их свободными и шум, распространяющийся на фон, который некоторые свободные ячейки преобразует в занятые.
Для этого был немного модифицирован генератор массива координат строк:
PHP
- // $this->noise инициализируется при создание объекта класса
- // может принимать значения от 0(нет шума) до 10
- $alphanum = 'abcdefghijkmnopqrstuvxyz0123456789';
- // основной цикл
- for ($i = 0; $i < $this->the_number_of_letters; ++$i) {
- $letter = $alphanum[intval(mt_rand(0, 33))];
- for ($j = 0; $j < count($this->abc[$letter]); $j += 2)
- // шум внутренний
- if (mt_rand(1, 100) > $this->noise*5)
- array_push($this->array_str, $this->abc[$letter][$j]+($i*7), $this->abc[$letter][$j+1]);
- // шум для фона
- for ($j = 0; $j < 7*7*($this->noise/20); ++$j) {
- array_push($this->array_str, mt_rand(1, 7)+($i*7), mt_rand(1, 7));
- }
- $_SESSION['di_captcha_str'] .= $letter;
- }
- return $this->shuffle2($this->array_str);
* This source code was highlighted with Source Code Highlighter.
В итоге:
Полностью рабочий пример
Основной класс:
PHP
- // В конструкторе задаётся длина строки символов, по умолчанию 6.
- // методы класса:
- // shuffle2() – перемешивает массив.
- // set() – может менять значения некоторых полей класса.
- // get() – возвращает массив координат и заносит в сессию саму строку.
- // check() – принимает введённый пользователем текст и сравнивает его со строкой, записанной в сессии.
- namespace di;
- class captcha {
- private $str, $array_str = array(), $abc = array(), $the_number_of_letters = 6, $noise = 1;
- function __construct($the_number_of_letters = 6) {
- $this->the_number_of_letters = $the_number_of_letters;
- $this->abc['a'] = array(4,1,3,2,5,2,3,3,5,3,2,4,6,4,2,5,3,5,4,5,5,5,6,5,1,6,7,6,1,7,7,7);
- $this->abc['b'] = array(1,1,2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,1,4,2,4,3,4,4,4,5,4,6,4,1,5,7,5,1,6,7,6,1,7,2,7,3,7,4,7,5,7,6,7);
- $this->abc['c'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,1,4,1,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['d'] = array(1,1,2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,1,4,7,4,1,5,7,5,1,6,7,6,1,7,2,7,3,7,4,7,5,7,6,7);
- $this->abc['e'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,1,2,1,3,1,4,2,4,3,4,4,4,1,5,1,6,1,7,2,7,3,7,4,7,5,7,6,7,7,7);
- $this->abc['f'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,1,2,1,3,1,4,2,4,3,4,4,4,1,5,1,6,1,7);
- $this->abc['g'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,1,4,1,5,5,5,6,5,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['h'] = array(1,1,7,1,1,2,7,2,1,3,7,3,1,4,2,4,3,4,4,4,5,4,6,4,7,4,1,5,7,5,1,6,7,6,1,7,7,7);
- $this->abc['i'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,4,2,4,3,4,4,4,5,4,6,1,7,2,7,3,7,4,7,5,7,6,7,7,7);
- $this->abc['j'] = array(7,1,7,2,7,3,7,4,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['k'] = array(1,1,7,1,1,2,5,2,6,2,1,3,3,3,4,3,1,4,2,4,1,5,3,5,4,5,1,6,5,6,6,6,1,7,7,7);
- $this->abc['l'] = array(1,1,1,2,1,3,1,4,1,5,1,6,1,7,2,7,3,7,4,7,5,7,6,7,7,7);
- $this->abc['m'] = array(1,1,7,1,1,2,2,2,6,2,7,2,1,3,3,3,5,3,7,3,1,4,4,4,7,4,1,5,7,5,1,6,7,6,1,7,7,7);
- $this->abc['n'] = array(1,1,7,1,1,2,2,2,7,2,1,3,3,3,7,3,1,4,4,4,7,4,1,5,5,5,7,5,1,6,6,6,7,6,1,7,7,7);
- $this->abc['o'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,1,4,7,4,1,5,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['p'] = array(1,1,2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,1,4,2,4,3,4,4,4,5,4,6,4,1,5,1,6,1,7);
- $this->abc['q'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,1,4,7,4,1,5,5,5,7,5,1,6,6,6,2,7,3,7,4,7,5,7,7,7);
- $this->abc['r'] = array(1,1,2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,1,4,2,4,3,4,4,4,5,4,6,4,1,5,5,5,1,6,6,6,1,7,7,7);
- $this->abc['s'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,2,4,3,4,4,4,5,4,6,4,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['t'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,4,2,4,3,4,4,4,5,4,6,4,7);
- $this->abc['u'] = array(1,1,7,1,1,2,7,2,1,3,7,3,1,4,7,4,1,5,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['v'] = array(1,1,7,1,1,2,7,2,2,3,6,3,2,4,6,4,3,5,5,5,3,6,5,6,4,7);
- $this->abc['w'] = array(1,1,7,1,1,2,7,2,1,3,7,3,1,4,4,4,7,4,1,5,3,5,5,5,7,5,1,6,2,6,6,6,7,6,1,7,7,7);
- $this->abc['x'] = array(1,1,7,1,2,2,6,2,3,3,5,3,4,4,3,5,5,5,2,6,6,6,1,7,7,7);
- $this->abc['y'] = array(1,1,7,1,2,2,6,2,3,3,5,3,4,4,4,5,4,6,4,7);
- $this->abc['z'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,6,2,5,3,4,4,3,5,2,6,1,7,2,7,3,7,4,7,5,7,6,7,7,7);
- $this->abc['0'] = array(3,1,4,1,5,1,2,2,6,2,1,3,5,3,7,3,1,4,4,4,7,4,1,5,3,5,7,5,2,6,6,6,3,7,4,7,5,7);
- $this->abc['1'] = array(4,1,3,2,4,2,2,3,4,3,4,4,4,5,4,6,1,7,2,7,3,7,4,7,5,7,6,7,7,7);
- $this->abc['2'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,7,3,6,4,4,5,5,5,2,6,3,6,1,7,2,7,3,7,4,7,5,7,6,7,7,7);
- $this->abc['3'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,7,3,5,4,6,4,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['4'] = array(5,1,4,2,5,2,3,3,5,3,2,4,5,4,1,5,2,5,3,5,4,5,5,5,6,5,7,5,5,6,5,7);
- $this->abc['5'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,1,2,1,3,2,3,3,3,4,3,5,3,6,3,7,4,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['6'] = array(3,1,4,1,5,1,6,1,2,2,1,3,1,4,2,4,3,4,4,4,5,4,6,4,1,5,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['7'] = array(1,1,2,1,3,1,4,1,5,1,6,1,7,1,6,2,5,3,4,4,3,5,2,6,1,7);
- $this->abc['8'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,2,4,3,4,4,4,5,4,6,4,1,5,7,5,1,6,7,6,2,7,3,7,4,7,5,7,6,7);
- $this->abc['9'] = array(2,1,3,1,4,1,5,1,6,1,1,2,7,2,1,3,7,3,2,4,3,4,4,4,5,4,6,4,7,4,7,5,6,6,2,7,3,7,4,7,5,7);
- }
-
- private function shuffle2($array) {
- for ($i = 0; $i < count($array); $i += 2)
- for ($j = count($array)-2; $j > $i; $j -= 2)
- if (mt_rand(0, 1) > 0) {
- $array[$i]+=$array[$j]; $array[$j]=$array[$i]-$array[$j]; $array[$i]-=$array[$j];
- $array[$i+1]+=$array[$j+1]; $array[$j+1]=$array[$i+1]-$array[$j+1]; $array[$i+1]-=$array[$j+1];
- }
- return $array;
- }
-
- function set($name, $val) {
- switch ($name) {
- case 'the_number_of_letters':
- $this->the_number_of_letters = (int)$val;
- break;
- case 'noise':
- $this->noise = (int)$val;
- break;
- default:
- return false;
- }
- return true;
- }
-
- function get() {
- $alphanum = 'abcdefghijkmnopqrstuvxyz0123456789';
- unset($_SESSION['di_captcha_str']);
- for ($i = 0; $i < $this->the_number_of_letters; ++$i) {
- $letter = $alphanum[intval(mt_rand(0, 33))];
- //foreach ($this->abc[$letter] as $key=>$val)
- // array_push($this->array_str, ($key%2 == 0)?$val+($i*7):$val);
- for ($j = 0; $j < count($this->abc[$letter]); $j += 2)
- if (mt_rand(1, 100) > $this->noise*5)
- array_push($this->array_str, $this->abc[$letter][$j]+($i*7), $this->abc[$letter][$j+1]);
- for ($j = 0; $j < 7*7*($this->noise/20); ++$j) {
- array_push($this->array_str, mt_rand(1, 7)+($i*7), mt_rand(1, 7));
- }
- $_SESSION['di_captcha_str'] .= $letter;
- }
- return $this->shuffle2($this->array_str);
- }
-
- function check($in_string) {
- echo $in_string.'|'.$_SESSION['di_captcha_str'];
- return (strtolower($in_string) == $_SESSION['di_captcha_str'])?true:false;
- }
- }
* This source code was highlighted with Source Code Highlighter.
После создания phar и сжатия его, получается файл di_captcha.class.phar.gz весом 3.53кб.
Прример использования класса:
PHP+html
// index.php
// Прример использования класса
session_start();
// THE_NUMBER_OF_LETTERS – константа, количество символов в строке
define('THE_NUMBER_OF_LETTERS', 6);
// При запросе от js о генерации строки, возвращается количество символов и сам массив координат.
if (isset($_POST['action']{14}) && $_POST['action'] == 'captcha_refresh') {
require 'phar://di_captcha.class.phar.gz/di_captcha.class.php';
$captcha = new di\captcha();
$captcha->set('noise', 0);
echo json_encode(array(THE_NUMBER_OF_LETTERS, $captcha->get()));
} else {
?><br><!DOCTYPE html><br><html><br><head><br> <meta charset='utf-8'><br> <title>Test</title><br> <link rel='stylesheet' media='all' href='style.css'><br> <script type='text/javascript' src='jquery-1.6.1.min.js' charset='utf-8'></script><br> src='script.js' charset='utf-8'></script><br></head><br><body><br> <p id='Title'>Сим-сим, откройся!</p><br> <p id='Msg'><br> <?php<br> if (isset($_POST['action']{11}) &#&&; $_POST['action'] == 'captcha_send') {;<br> require 'phar://di_captcha.class.phar.gz/di_captcha.class.php';<br> $captcha = new di\captcha();<br> echo ($captcha->check($_POST['text_captcha']))?'Сим-сим открылся!':'К сожалению, Вы ошиблись...';<br> }<br> ?><br> </p><br> <form action='index.php' method='post'><br> <div id='DICaptchaPic'></div><br> <p style='padding: 0 10px;'><br> <input type='text' name='text_captcha' id='text_captcha' value='<?php echo $_POST['text_captcha']; ?>' placeholder='6 символов с картинки'><br><label for='text_captcha'>*aнти-спам</label> <ahref='#' onclick='di_captcha_refresh(); return false;'>Не вижу</a><br> </p><br> <p style='padding: 10px 0;'><br> <input type='hidden' name='action' value='captcha_send' /><br> <input type='submit' name='submit' value='Проверить' /><br> </p><br> </form><br></body><br></html><br><?php<br> }<br>* This source code was highlighted with Source Code Highlighter.
При запросе от js о генерации строки, возвращается количество символов и сам массив координат.
И, собственно, js скрипт:
JS
- /* script.js */
- /* cell_size – размер ячеек в пикселях */
- var cell_size = 3;
- function di_captcha_refresh() {
- $.post('./index.php', {action: 'captcha_refresh'},
- function(data) {
- var data = eval(data);
- $('#DICaptchaPic').css('width', ((((cell_size+2)*6)+(3*cell_size)+1)*data[0]));
- var html_p_tag = '';
- for (i = 1; i <= 7*7*data[0]; ++i) {
- var style = (i%7 == 0)?'margin-right: '+2*cell_size+'px;':'';
- for (j = 0; j < data[1].length; j += 2) style +=(((i%(data[0]*7)==0)?(data[0]*7):i%(data[0]*7)) == data[1][j] && Math.ceil(i/(data[0]*7)) == data[1][j+1])?'background-color: #000;':'';
- html_p_tag += '<p'+((style=='')?'':' style=\''+style+'\'')+'>'
} $('#DICaptchaPic').html(html_p_tag); } )} $(document).ready(function() { $('#DICaptchaPic').css('overflow', 'hidden'); $('#DICaptchaPic').css('height', (cell_size+2)*7); di_captcha_refresh(); $('#DICaptchaPic').click(function() {di_captcha_refresh();}); $('#text_captcha').focus()})
* This source code was highlighted with Source Code Highlighter.
P.S.
1. Если в класс генерации строк добавить шаблоны символов, по несколько шаблонов на символ, то это ещё больше усложнит расшифровку посредствам шаблонов.
2. Работники _http://decaptcher.com, _http://captchabot.com и _http://antigate.com/ скажут вам спасибо за такую капчу.
3. Эта статья была опубликована моим другом, в разделе веб разработка несколько дней назад, набрала 16 минусов, 14 плюсов и 11 коментов. Потом была похоронена модераторами по причине «не умеет читать правила сайта (публикует чужие посты и клянчит инвайты)», статья написана специально для сайта, и инвайты ни кто не клянчил, а уточнили, что он не автор статьи. Потом попала в песочницу и заслужила инвайт.
4. Краткий смысл 11 комментариев:
— чтобы обойти капчу надо перехватить post запрос и расшифровать текст по шаблонам
— с шумом при расшифровки будет большой брак
— с шумом буквы «О» и «D», «C» и «G» иногда трудно разборчивы и путаються, их лучше исключить из алфавита
— расшифровать можно любую капчу, главное найти компромисс между читаемостью и сложностью обхода
Скачать исходники
Демо: без шума, с шумом(1)
UPD: nagato разработал скрипт который удачно обходит капчу в 70% случаев.