Как стать автором
Обновить

SimpleMvcCaptcha, или мой первый опыт Open Source

Время на прочтение5 мин
Количество просмотров1.5K
Disclaimer!
Я ни в коем случае не претендую на звание разработчика идеальной капчи, равно как и на изгобретателя чего-то нового. Все, что здесь написано, было сделано мной для познавательных целей и open source. И да, я осознаю, что написать свою капчу — это изобрести велосипед.

С чего все началось


Совсем недавно я начал разрабатывать свой пока небольшой проект на ASP.NET MVC 3. Суть его заключается в том, что посетители могут добавлять на него свои сообщения, которые впоследствии появляются в публичном доступе (кому интересно, что стоит за этим запутанным объяснением — ссылка будет в конце).

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

Первое, что пришло мне в голову, была ReCaptcha от Google. Установив ее и некоторое время попользовавшись, я окончательно понял, что этот монстр — совсем не для меня и не для большинства адекватных людей, тем более русскоязычных (вывод некоторых изображений не только не читается машинно, он и людьми то не может быть прочитан). Поискав другие решения, я, к своему сожалению, не нашел чего-то нормального для MVC 3, простого и ненавязчивого в использовании. Были разные мануалы как сделать то или иное, но готового решения «взял и использовал» как-то не встретилось. Поэтому решил изобрести велосипед написать свою капчу.

Идея


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

Реализация


Сказано — сделано. Недолго думая я создал в студии новый проект сборки (оформить капчу для одного единственного проекта в виде отдельной dll мне подсказало мое чувство хорошего тона и, как оказалось, не зря). В нем я создал класс с обычным методом расширения для HtmlHelper класса. Весь проект так и назвал тогда — SimpleMvcCaptcha.

Суть реализации такова. Хелпер случайно генерит два числа-операнда и одну операцию (пока только + или -). Исходя из этих параметров вычисляется результат. Затем происходят два финта ушами. Первый — мы должны создать изображение с выражением капчи. Однако внутри хелпера этого сделать нельзя. Поэтому нам необходимо создать тег img, источником картинки для которого станет специально подготовленный экшен специального контроллера, о котором ниже. Второй финт ушами — нам надо как-то сохранить информацию о результате, чтобы при последующем посте результатов на сервер не заниматься распознаванием собственной же капчи.

Сначала я хотел пойти по немного нестандартному пути в виду отсутствия богатого опыта. Дело в том, что большинство капчей, про которые я читал, передавали свои значения через кеш или сессии. Мне не очень понравилась такая идея, поэтому я решил хранить результат операции внутри html самой страницы с капчей в hidden поле. Но чтобы не упрощать жизнь анализаторам, данную строку я решил шифровать с помощью AES. Однако скоро мне помогли осознать, что в таком случае боту ничего не стоит подменить как хеш картинки, так и результат для нее, что моментально разрушит стойкость. Поэтому я все же пошел по пути большинства.

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

    /// <summary>
    /// Captcha object
    /// </summary>
    internal class Captcha
    {
        /// <summary>
        /// Result of captcha's expression
        /// </summary>
        public string Result { get; internal set; }

        /// <summary>
        /// Captcha's expression
        /// </summary>
        public string Expresion { get; internal set; }
    }


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

    public class CaptchaController : Controller
    {
        public FileContentResult GetImage(string id)
        {
            return File(CaptchaUtils.GetImage(id), "image/gif");
        }
    }


Валидация происходит тоже довольно просто:
        /// <summary>
        /// Validates input
        /// </summary>
        /// <returns>true - validation succeeded \nfalse - validation failed</returns>
        public static bool Validate()
        {
            var ctx = HttpContext.Current;

            var captchaAnswer = ctx.Request.Form["captchaAnswer"];
            var captchaHidden = ctx.Request.Form["captchaId"];

            // If input is not empty
            if(!String.IsNullOrEmpty(captchaAnswer) && !String.IsNullOrEmpty(captchaHidden))
            {
                var captcha = ctx.Session[captchaHidden] as Captcha;
                ctx.Session.Remove(captchaHidden);

                return captcha != null && captchaAnswer == captcha.Result;
            }

            return false;
        }


Что получилось


image

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

Клиентский код выглядит так:
<div class="smc-captcha">
  <img src='/Captcha/GetImage/08a75516-f1ed-41ca-a926-724a268f171e' alt='captcha' class='smc-img-captcha' ><br/>
  <input type='hidden' name='captchaId' value='08a75516-f1ed-41ca-a926-724a268f171e' />
  <input type='text' name='captchaAnswer' class='smc-input-result' />
</div>


Кастомизация


Большинство параметров, используемых в процессе генерации капчи, можно переопределить через web.config вашего ASP.NET MVC проекта. Из таких параметров можно выделить ширину и высоту изображения, размер и тип шрифта, текст-замена для + и — (на картинке сверху это третий кадр), максимальное число для использования в выражениях, наименование контроллера и экшена для выдачи картинки. Также можно кастомизировать CSS свойства элементов div, img и imput с помощью переопределения соответствующих классов.

Open Source


Теперь то, почему я решил писать на хабр. Данная статья в своем названии упоминает про Open Source. Да, после того, как я реализовал данную капчу для себя, я решил, что неплохо было бы поделиться ей и с остальным сообществом. Это мой первый опыт open source разработки, поэтому он мне стал вдвойне интересен. Весь код, описание и документация выложены на CodePlex по адресу http://simplemvccaptcha.codeplex.com/ под лицензией GPLv2. Заходите, качайте, пользуйтесь.

В заключение


Я очень надеюсь, что этот небольшой проект поможет вам при создании сайтов на основе ASP.NET MVC 3, в которых будет нужен функционал несложной капчи, которую легко распознать человеку, но поможет оградить от всяких ботов.

P.S. А тот проект, для которого это изначально писалось и о котором я говорил в начале, называется "Факты о программировании". Там пока не так много фактов, но я надеюсь, что в том числе и с вашей поддержкой проект будет активно развиваться!
Пример использования капчи можно найти на странице добавления своего факта. Убедительная просьба — если Вы заметили баг в капче или сайте, не пытайтесь его тут же сломать. Лучше сообщите мне и я его исправлю. Давайте будем конструктивными.
Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+3
Комментарии5

Публикации

Изменить настройки темы

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн