3

I want to add an "anti-robot" question to the CreateUserWizard as a more accessible alternative to a Captcha control. I'm fairly new to asp and finding that I'm a bit stuck in a WinForms mindset. However, I have come up with something that appears to work.

Markup:

    <asp:CreateUserWizard ID="CreateUserWizard1" runat="server">
    .
    .
    <tr>
      <td align="right">
        <asp:Label ID="AntiRobotQuestion" runat="server" AssociatedControlID="AntiRobotAnswer">
          Question:
        </asp:Label>
      </td>
      <td>
        <asp:TextBox ID="AntiRobotAnswer" runat="server"></asp:TextBox>
        <asp:RequiredFieldValidator ID="AntiRobotAnswerRequired" runat="server" ControlToValidate="AntiRobotAnswer" ErrorMessage="Answer is required." ToolTip="Answer is required." ValidationGroup="CreateUserWizard1">
        </asp:RequiredFieldValidator>
      </td>
    </tr>
    <tr>
      <td align="center" colspan="2" style="color:Red;">
        <asp:Literal ID="CustomErrorMessage" runat="server" Visible="False" EnableViewState="False"></asp:Literal>
      </td>
    </tr>
  .
  .
  </asp:CreateUserWizard>

Code behind:

protected void Page_Load(object sender, EventArgs e)
{

    if (!IsPostBack) {
        //Set up the Anti-Robot Question and Answer
        Label robotQuestion = (Label)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("AntiRobotQuestion");
        //Simulate randomly selecting a question and answer from a database table...
        robotQuestion.Text = "What is the capital of France";
        Session["AntiRobotAnswer"] = "Paris";
    }

}

protected void CreateUserWizard1_CreatingUser(object sender, LoginCancelEventArgs e)
{
    //Check the anti-robot Q & A
    TextBox robotAnswer = (TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("AntiRobotAnswer");
    if (robotAnswer.Text != (string)Session["AntiRobotAnswer"])
    {
        Literal errorMessage = (Literal)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("CustomErrorMessage");
        errorMessage.Text = "Wrong answer! Are you a robot?";
        errorMessage.Visible = true;
        e.Cancel = true;
    }

}

Is this an acceptable way to code this? Two things in particular look a bit "untidy" to me:

  1. The use of FindControl to pull out references to controls in the markup.
  2. Storing the expected answer in a session variable. (How secure is it?)

EDIT (2012-01-23) Some valid design alternatives have been given. However, I have a valid reason to use this question and answer technique (possibly in addition to the honeypot idea). For example, a question relevant to the subject of a forum can help to prevent human spammers as well as bots. The question is: is the code outlined above an acceptable way to do this? Coming from a WinForms background, it looks a bit clunky to me - but maybe that's what asp is supposed to look like.

Fruitbat
  • 764
  • 2
  • 5
  • 19
  • 1
    Another option to a Session variable is to store a *hash* of the answer in the ViewState/ControlState. I say store the hash, not the answer, because the ViewState is not always fully encrypted, but *should* always be guarded from tampering via the [ViewState MAC](http://www.testingreflections.com/node/view/3424) (of course some silly person could have disabled this...) –  Jan 18 '12 at 23:50
  • 2
    I do not like this idea... better make the honeypot captcha http://haacked.com/archive/2007/09/11/honeypot-captcha.aspx – Aristos Jan 18 '12 at 23:55
  • I like both of these alternative ideas. – Fruitbat Jan 20 '12 at 00:02
  • @pst: Presumably I can generate a javascript function to hash the input and compare, avoiding a postback? I will have to experiment with this. – Fruitbat Jan 20 '12 at 00:07
  • @Aristos: That looks like a seriously cool idea. My only worry would be that some bots would not be clever enough to be fooled. Again, I will have to investigate further. – Fruitbat Jan 20 '12 at 00:07
  • @Fruitbat Don't hash in JS. Hash the correct answer and store it in the ViewState/ControlState. Then on the postback, hash the answer and ensure it results in the same value as the previously hashed answer. This approach loses the "correct response" (just as password hashing "loses" the original password), so you can only say "Wrong", not "The capital of France is Paris". If you hash in JS then, assuming the ViewState is not encrypted, the client (which should not be trusted!) could just send back the stored hash value -- without actually hashing anything! –  Jan 20 '12 at 19:18
  • @Fruitbat Of course, it should be pointed out, that a fast hash like SHA1 can be brute-forced in a matter of minutes (with a moderately cheap GPU setup) for a small range such as 8 characters, even excluding the idea of rainbow tables being used. If only valid words/names are in the domain then it is a *much* easier problem to brute force. So just know my proposed idea increases the difficulty -- without storing stuff server-side only -- but does not make it impossible. Encrypting the ViewState could eliminate this issue, as can additional hardening techniques, or just ... Session state. –  Jan 20 '12 at 19:32
  • @pst: When I said hash in JS I meant generate the hashed answer on the server and generate a JS function to compare a hash of the input with this hashed answer (assuming same algorithm on client and server). If a postback is required, then why send the answer to the client at all (hashed, encrypted or whatever). – Fruitbat Jan 23 '12 at 23:23
  • @Fruitbat Never trust the client: this will be the first thing an attacker can *trivially bypass*. The idea to send it to the client is so it can be *verified* on postback *without* requiring the using of *Session state* (Session state is good, and required for certain tasks, but one of the overlooked features of ASP.NET is the strong ViewState support). Because it *is* sent to the client, precautions must be done, as noted above, along with limitations. (This is why the plain-text answer is *not sent*, unless the ViewState can be *guaranteed* to be strongly encrypted.) –  Jan 24 '12 at 03:47

1 Answers1

2

As I say, I do not like the idea of you to ask for Paris.

  1. The simplest way is to use a non visible field and see if a bot fill it with data, the honeypot idea http://haacked.com/archive/2007/09/11/honeypot-captcha.aspx

  2. also you can use the NoBot from asp.net toolkit http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/NoBot/NoBot.aspx

  3. There are many other ideas on this SO article Practical non-image based CAPTCHA approaches?

Community
  • 1
  • 1
Aristos
  • 66,005
  • 16
  • 114
  • 150
  • I really like you're "click captcha" idea from the alternate. That is actually and idea I've had for awhile, although it is hard for certain demographics... –  Jan 20 '12 at 19:25
  • I believe this is a viable design alternative (or even addition) but the question is more concerned with coding than design. I will add to the original question. – Fruitbat Jan 23 '12 at 23:26