Валидация капчи после обхода сервера и последующего повторного генерирования капчи - PullRequest
2 голосов
/ 03 февраля 2010

Я внедряю CAPTCHA в форму представления согласно книге Сандерсона Pro ASP.NET MVC Framework .

Поля просмотра генерируются с:

<%= Html.Captcha("testCaptcha")%>
<%= Html.TextBox("attemptCaptcha")%>

Помощник VerifyAndExpireSolution не работает, поскольку его решение реализовано.

Я добавляю проверку, и когда она не срабатывает, я добавляю сообщение об ошибке ModelState и отправляю пользователя обратно в представление, как указано в книге:

return ModelState.IsValid ? View("Completed", appt) : View();

Но при этом генерируется новый GUID, который генерирует новый текст CAPTCHA.

Однако проблема заключается в том, что значение скрытого поля CAPTCHA и URL-адрес изображения CAPTCHA сохраняют оригинальный GUID. Таким образом, вы никогда не сможете ввести правильное значение. У вас есть только один выстрел, чтобы сделать это правильно.

Я новичок во всем этом, но это как-то связано с видом, сохраняющим значения из первой загрузки страницы.

Captcha генерируется с:

public static string Captcha(this HtmlHelper html, string name)
{
    // Pick a GUID to represent this challenge
    string challengeGuid = Guid.NewGuid().ToString();
    // Generate and store a random solution text
    var session = html.ViewContext.HttpContext.Session;
    session[SessionKeyPrefix + challengeGuid] = MakeRandomSolution();

    // Render an <IMG> tag for the distorted text,
    // plus a hidden field to contain the challenge GUID
    var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
    string url = urlHelper.Action("Render", "CaptchaImage", new{challengeGuid});
    return string.Format(ImgFormat, url) + html.Hidden(name, challengeGuid);
}

А потом я пытаюсь проверить это с помощью:

public static bool VerifyAndExpireSolution(HttpContextBase context,
                                       string challengeGuid,
                                       string attemptedSolution)
{
    // Immediately remove the solution from Session to prevent replay attacks
    string solution = (string)context.Session[SessionKeyPrefix + challengeGuid];
    context.Session.Remove(SessionKeyPrefix + challengeGuid);

    return ((solution != null) && (attemptedSolution == solution));
}

А как насчет воссоздания имен целевых полей с помощью guid? Тогда каждое поле уникально и не сохранит значения предыдущих поколений форм?

Или мне просто нужна другая реализация CAPTCHA?

Ответы [ 2 ]

1 голос
/ 13 августа 2011

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

Одно из решений - заставить браузер обновлять страницу при перезагрузке после неудачной попытки; этого не произойдет, если вы просто вернете View (). Вы можете сделать это, используя RedirectToAction («SubmitEssay»), который поразит метод действия, принимающий HttpVerbs.Get.

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

Итак, следуя примеру книги,

if (!CaptchaHelper.VerifyAndExpireSolution(HttpContext, captcha, captchaAttempt) 
{
    RedirectToAction("SubmitEssay", new { fail = 1 });
}

Затем просто проверьте, содержит ли коллекция QueryString сообщение «ошибка», чтобы доставить сообщение об ошибке.

0 голосов
/ 03 февраля 2010

Итак, я решил реализовать reCaptcha. И я настроил свой взгляд также:

<div id="recaptcha_image"></div>&nbsp;
     <a href="#" onclick="Recaptcha.reload();">
            generate a new image
     </a><br />
<input type="text" name="recaptcha_response_field" 
           id="recaptcha_response_field" />
           &nbsp;<%= Html.ValidationMessage("attemptCaptcha")%>
<script type="text/javascript" 
     src="http://api.recaptcha.net/challenge?k=[my public key]"></script>

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

<style type="text/css">
    #recaptcha_widget_div {display:none;}
</style>

Затем в моем контроллере я просто должен проверить captchaValid:

[CaptchaValidator]
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult SubmitEssay(Essay essay, bool acceptsTerms, bool captchaValid)
{
    if (!acceptsTerms)
        ModelState.AddModelError("acceptsTerms", 
                     "You must accept the terms and conditions.");
    else
    {
       try
       {
            // save/validate the essay
            var errors = essay.GetRuleViolations(captchaValid);
            if (errors.Count > 0)
                throw new RuleException(errors);

        }
        catch (RuleException ex)
        {
            ex.CopyToModelState(ModelState, "essay");
        }
    }
    return ModelState.IsValid ? View("Completed", essay) : View();
}

public NameValueCollection GetRuleViolations(bool captchaValid)
{
    var errors = new NameValueCollection();
    if (!captchaValid)
        errors.Add("attemptCaptcha", 
             "Please enter the correct verification text before submitting.");
    // continue with other fields....
}

И все это предполагает, что вы реализовали атрибут Action Filter и помощник вида, как описано в recaptcha.net:

public class CaptchaValidatorAttribute : ActionFilterAttribute
{
    private const string CHALLENGE_FIELD_KEY = "recaptcha_challenge_field";
    private const string RESPONSE_FIELD_KEY = "recaptcha_response_field";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var captchaChallengeValue = 
             filterContext.HttpContext.Request.Form[CHALLENGE_FIELD_KEY];
        var captchaResponseValue = 
             filterContext.HttpContext.Request.Form[RESPONSE_FIELD_KEY];
        var captchaValidtor = new Recaptcha.RecaptchaValidator
          {
              PrivateKey = "[my private key]",
              RemoteIP = filterContext.HttpContext.Request.UserHostAddress,
              Challenge = captchaChallengeValue,
              Response = captchaResponseValue
          };

        var recaptchaResponse = captchaValidtor.Validate();

    // this will push the result value into a parameter in our Action
        filterContext.ActionParameters["captchaValid"] = recaptchaResponse.IsValid;

        base.OnActionExecuting(filterContext);
    }
}

Помощник HTML:

public static class Captcha
{
    public static string GenerateCaptcha( this HtmlHelper helper )
    {  
    var captchaControl = new Recaptcha.RecaptchaControl
        {
            ID = "recaptcha",
            Theme = "clean",
            PublicKey = "[my public key]",
            PrivateKey = "[ my private key ]"
        };
    var htmlWriter = new HtmlTextWriter( new StringWriter() );
        captchaControl.RenderControl(htmlWriter);
    return htmlWriter.InnerWriter.ToString();
    }
}

Надеюсь, это поможет кому-то, кто застрял с реализацией в книге.

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