An easy fix to the single question/answer shortfall with the Membership Provider

Using question/answer to recover a forgotten password is a normal practice on the web. Unfortunately the canned Membership providers use only one question and answer.  In reality, you have the answer being a PASSWORD – a very very weak password very easy to attack with a dictionary attack.

 

There is an easy fix without having to write your own membership provider. You stuff as many questions as you want into the existing field using a tab (\t) as a delimiter. Tabs can never be entered from a single line text box so there is no risk of a question or answer containing a tab (\t).

 

The extensions below allows you to have as many questions as you wish using the same old providers!

 

/// <summary>
/// Delimited is a tab. This cannot be entered in a web text box
/// </summary>
private static char[] MembershipUserQuestionSeperator = { '\t' };

/// <summary>
/// Gets the questions to be asked
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static string[] GetQuestions(this MembershipUser user)
{
    return user.PasswordQuestion.Split(MembershipUserQuestionSeperator, StringSplitOptions.RemoveEmptyEntries);
}

/// <summary>
/// Retrieves the password via a set of questions
/// </summary>
/// <param name="provider"></param>
/// <param name="username"></param>
/// <param name="answers"></param>
/// <returns></returns>
public static string GetPassword(this MembershipProvider provider, string username, string[] answers)
{
    var answer = new StringBuilder();
    for (int i = 0; i < answers.Length; i++)
    {
        answer.Append(answers[i]);
        if (i < answers.Length - 1)
            answer.Append(MembershipUserQuestionSeperator[0]);
    }
    return provider.GetPassword(username, answers.ToString());
}

public static bool ChangePasswordQuestionAndAnswer(this MembershipUser user, string password, string[] questions, string[] answers)
{
    if (questions.Length != password.Length)
    {
        throw new ArgumentException("Mismatched numbers of questions and answers");
    }

    var question = new StringBuilder();
    var answer = new StringBuilder();
    for (int i = 0; i < answers.Length; i++)
    {
        answer.Append(answers[i]);
        question.Append(questions[i]);
        if (i < answers.Length - 1)
        {
            answer.Append(MembershipUserQuestionSeperator[0]);
            question.Append(MembershipUserQuestionSeperator[0]);
        }
    }
    return user.ChangePasswordQuestionAndAnswer(password, question.ToString(), answer.ToString());
}

For the Sql Membership Provider, you should increase the column size, such as shown below.

 

Alter Table dbo.aspnet_Membership Alter Column PasswordQuestion nvarchar(max)
Go
Alter Table dbo.aspnet_Membership Alter Column PasswordAnswer nvarchar(max)
Go

The default length is 256 characters for questions which will usually handle at least 5 typical questions. The default length is 128 characters for answers, enough for 11 answers of 10 characters each.

 

How many questions should you have?

The typical person has a working vocabulary of some 1000 words. Taking  a-zA-Z0-9 + special characters, we have ~100 characters. So 6 characters is 10^12 odds ( (10^2)^6). To get the same security with answers, it means 4 questions ((10^3)^4). So questions and answers should use four questions.

Comments

Popular posts from this blog

Yet once more into the breech (of altered programming logic)

How to convert SVG data to a Png Image file Using InkScape

Simple WP7 Mango App for Background Tasks, Toast, and Tiles: Code Explanation