代码之家  ›  专栏  ›  技术社区  ›  Scott

在服务器往返和随后重新生成验证码后验证验证验证码

  •  2
  • Scott  · 技术社区  · 15 年前

    我正在按照桑德森的书在我的表格提交中实现验证码。 pro asp.net mvc框架 .

    视图字段由以下项生成:

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

    VerifyandExpireSolution帮助程序在实现其解决方案时不起作用。

    我正在添加验证,当验证失败时,我将添加一条modelstate错误消息,并将用户发送回本书中所述的视图:

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

    但是,这样做,会生成一个新的guid,生成新的验证码文本。

    但问题是,captcha隐藏字段值和captcha图像url都保留 起初的 GUID。所以,您永远无法输入正确的值。你基本上只有一次机会把事情做好。

    我对所有这些都不熟悉,但这与视图保留第一页加载时的值有关。

    生成验证码时使用:

    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重新构建目标字段名怎么样?那么,每个字段都是唯一的,不会保留前几代表单的值吗?

    或者我只需要一个不同的验证码实现?

    2 回复  |  直到 13 年前
        1
  •  1
  •   diego    13 年前

    我在使用桑德森书中的验证码时也遇到了同样的问题。问题是页面被浏览器缓存,并且在验证码测试失败后不会刷新。所以它总是显示相同的图像,即使生成并存储了新的验证码以供测试。

    一种解决方案是在尝试失败后重新加载时强制浏览器刷新页面;如果只返回view(),则不会发生这种情况。您可以使用redirectToAction(“submitseSay”)执行此操作,该操作将命中接受httpverbs.get的action方法。

    当然,您将失去使用viewdata通知用户错误的能力,但您可以将其包含在查询字符串中,然后选中查询字符串以显示消息。

    所以,以这本书为例,

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

    然后检查querystring集合是否包含“fail”以传递错误消息。

        2
  •  0
  •   Scott    15 年前

    所以,我决定实施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>
    

    这将创建两个CAPTCHA—一个在我的图像容器中,另一个由脚本创建。所以,我添加了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属性和view helper,详见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();
        }
    }
    

    希望这能帮助那些被书中的实现困住的人。