I'm implementing CAPTCHA in my form submission as per Sanderson's book Pro ASP.NET MVC Framework.
The view fields are generated with:
<%= Html.Captcha("testCaptcha")%>
<%= Html.TextBox("attemptCaptcha")%>
The VerifyAndExpireSolution helper is not working as his solution is implemented.
I'm adding validation and when it fails I add a ModelState error message and send the user back to the view as stated in the book:
return ModelState.IsValid ? View("Completed", appt) : View();
But, doing so, generates a new GUID which generates new CAPTCHA text.
The problem is, however, that the CAPTCHA hidden field value and the CAPTCHA image url both retain the original GUID. So, you'll never be able to enter the correct value. You basically only have one shot to get it right.
I'm new to all of this, but it has something to do with the view retaining the values from the first page load.
Captcha is generated with:
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);
}
And then I try to validate it with:
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));
}
What about re-building the target field names with the guid? Then, each field is unique and won't retain the previous form generations' value?
Or do I just need a different CAPTCHA implementation?