2010-02-02 3 views
1

Je suis en train d'implémenter CAPTCHA dans ma soumission de formulaire selon le livre de Sanderson Pro ASP.NET MVC Framework.Validation du Captcha après l'aller-retour du serveur et la nouvelle génération du captcha

Les champs d'affichage sont générés avec:

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

L'aide VerifyAndExpireSolution ne fonctionne pas comme sa solution est mise en œuvre.

J'ajoute la validation et quand il échoue j'ajouter un message d'erreur de ModelState et envoyer l'utilisateur à la vue comme indiqué dans le livre:

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

Mais, ce faisant, génère un nouveau GUID qui génère un nouveau texte CAPTCHA.

Le problème est cependant que la valeur du champ masqué CAPTCHA et l'URL de l'image CAPTCHA conservent tous les deux le original GUID. Ainsi, vous ne serez jamais capable d'entrer la valeur correcte. Vous n'avez essentiellement qu'un seul coup pour bien faire les choses. Je suis nouveau dans tout cela, mais cela a quelque chose à voir avec la vue qui retient les valeurs du chargement de la première page.

Captcha est généré avec:

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); 
} 

Et puis je tente de le valider avec:

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)); 
} 

Qu'en est re-construire les noms de champ cible avec le guid? Ensuite, chaque champ est unique et ne conservera pas la valeur des générations précédentes. Ou ai-je juste besoin d'une implémentation CAPTCHA différente? J'ai donc décidé d'implémenter reCaptcha.

+0

Le nom de champ unique n'a pas fonctionné. J'ai vécu la même chose. Le champ cible n'est créé que la première fois et ensuite, il est mis en cache et non régénéré à nouveau. – Scott

+0

C'est probablement pourquoi une implémentation basée sur javascript, comme reCaptcha est probablement meilleure. – Scott

Répondre

0

Et je l'ai personnalisé mon avis aussi:

<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> 

Cela crée deux un captchas- dans mon conteneur d'image, et un autre créé par le script. Donc, j'ai ajouté css pour cacher l'auto-généré un:

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

Puis, dans mon contrôleur, je dois simplement tester 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.... 
} 

Et tout cela suppose que vous avez mis en œuvre l'attribut de filtre d'action et l'aide de vue comme détaillé à 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); 
    } 
} 

aide 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(); 
    } 
} 

Espérons que cela aide quelqu'un qui est resté coincé avec l'implémentation dans le livre.

1

J'ai eu le même problème en utilisant l'exemple captcha du livre de Sanderson. Le problème est que la page est mise en cache par le navigateur et ne s'actualise pas après l'échec du test captcha. Il affiche donc toujours la même image, même si un nouveau captcha a été généré et stocké pour être testé.

Une solution consiste à forcer le navigateur à actualiser la page lors du rechargement après une tentative échouée; Cela n'arrivera pas si vous venez de retourner View(). Vous pouvez le faire en utilisant RedirectToAction ("SubmitEssay") qui va frapper la méthode d'action acceptant HttpVerbs.Get.

Bien sûr, vous perdez la possibilité d'utiliser ViewData pour avertir votre utilisateur de l'erreur, mais vous pouvez simplement l'inclure dans la chaîne de requête, puis vérifier la chaîne de requête pour afficher votre message.

Ainsi, suivant l'exemple du livre,

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

Ensuite, il suffit de vérifier si la collection QueryString contient « échec » pour votre message d'erreur.