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)

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

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