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
Post a Comment