13

Does anyone have a solution (sample code) for the following features:

  • Create a randomGuid/Cryptographically strong random number
  • Send a unique URL containing the random number to the user's email address
  • When confirmed, the user is asked to change password

My provider is currently parametrized this way:

enablePasswordRetrieval="false" 
enablePasswordReset="true" 
requiresQuestionAndAnswer="false" 
applicationName="/" 
requiresUniqueEmail="true" 
passwordFormat="Hashed" 
maxInvalidPasswordAttempts="5" 
minRequiredPasswordLength="5" 
minRequiredNonalphanumericCharacters="0" 
passwordAttemptWindow="10" 
passwordStrengthRegularExpression="" 
name="AspNetSqlMembershipProvider"

The security issues with this type of procedure have been discussed here before.

Community
  • 1
  • 1
pelican_george
  • 961
  • 2
  • 13
  • 33
  • Have a look at the crypto rng providers in System.Security.Cryptography to generate your random numbers and use a simple map to convert them to alphanumeric characters - all lower case, skip the letters i, l and o to avoid confusion (may also want to skip all vowels to avoid nasty words). Store email address and random key in database and create simple page that captures the new password etc and uses key to look up user and then call user.ChangePassword(mu.ResetPassword(), password) to change the password. – Neal Jul 08 '10 at 01:52
  • 1
    You can find good solution in detail on http://www.4guysfromrolla.com/articles/062508-1.aspx – AmirHossein Sep 14 '12 at 15:16

2 Answers2

5

Rui, I was in a similar boat a few moths ago, and not having had a great deal of exposure to the membership provider before, I struggled to get it implemented properly, because I thought you have to implement and use the whole thing as is, or nothing at all.

Well I soon discovered that you can only use parts of it, not the entire object. For example in my application I have my own user object and my own password and login modules, but I needed to get forms authentication working. So I basically just use the Membership provider for that part. I am not overriding any methods or anything, just use it for FormsAuthentication, then I use my own repositories to changing passwords, verifying username passwords etc.

Here is a snippet from my login code in MVC.

   var authTicket =  new FormsAuthenticationTicket(1, user.UniqueName, DateTime.UtcNow, DateTime.UtcNow.AddHours(2), rememberMe, cookieValues);
    var encryptedTicket = FormsAuthentication.Encrypt(authTicket);
    var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) { Expires = DateTime.UtcNow.AddDays(14) };

    httpResponseBase.Cookies.Add(authCookie);

This enables me to use the FormsAuthentication.SignOut(); methods and other methods the membership provider makes available.

On your question re Create a randomGuid/Cryptographically strong random number, here is a good method you can use, I found it a while ago, and I am sorry but can't remember where on the web

/// <summary>
/// A simple class which can be used to generate "unguessable" verifier values.
/// </summary>
public class UnguessableGenerator
{
    const string AllowableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/^()";
    const string SimpleAllowableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    /// <summary>
    /// Generates an unguessable string sequence of a certain length
    /// </summary>
    /// <param name="length">The length.</param>
    /// <param name="useSimpleCharacterSet">if set to <c>true</c> [use simple character set].</param>
    /// <returns></returns>
    public static string GenerateUnguessable(int length, bool useSimpleCharacterSet = false, string additionalCharacters = "")
    {
        var random = new Random();

        var chars = new char[length];

        var charsToUse = useSimpleCharacterSet ? SimpleAllowableCharacters : AllowableCharacters;
        charsToUse = string.Format("{0}{1}", charsToUse, additionalCharacters);

        var allowableLength = charsToUse.Length;

        for (var i = 0; i < length; i++)
        {
            chars[i] = charsToUse[random.Next(allowableLength)];
        }

        return new string(chars);
    }




    /// <summary>
    /// Generates an ungessable string, defaults the length to what google uses (24 characters)
    /// </summary>
    /// <returns></returns>
    public static string GenerateUnguessable()
    {
        return GenerateUnguessable(24);
    }
}

For Send a unique URL containing the random number to the user's email address, what you need is in your User table or any table a way to store a random id and then use that in a crafted url in an email that the user can use to verify.

This will require you to create a Url that will accept that type of Url, and then you have to extract the parts you need form the querystring and update your user object.

For When confirmed, the user is asked to change password, by default have a bit flag in your table that forces a user to change his password, this will come in handy later too if you need to force a user to change password. So then your login code looks to see if this bit flag is on, and if so, it will force a password change first. Then once the user has changed his password, you will turn this flag off.

Hope this helps.

Ryk
  • 3,072
  • 5
  • 27
  • 32
  • 2
    Downvoted for the abuse of Thread.Sleep in the code sample - if you want truly random data, use the crypto rng providers. – Neal Jul 08 '10 at 01:40
  • There, removed it. Once you allow for race conditions, you realize there is no 'true' random data. – Ryk Jul 08 '10 at 04:34
  • Use Guid, why goto all this trouble, when a Guid is guaranteed to be unique? – Eric Brown - Cal Nov 17 '10 at 16:46
1

At least use the Guid class for the guid. You may be able to customize the result

var guid = Guid.NewGuid();

..... To send the link

var msg = new MailMessage();
msg.From = new MailAddress("YOUR_ADDRESS");
msg.To.Add(new MailAddress("CLIENT_ADDRESS"));
msg.Subject = "YOUR SUBJECT";
msg.Body = @" SOME TXT.... http:\\resetPassword.aspx?guid=" + guid;
var smtpClient = new SmtpClient("SMTP_SERVER", 9990);
smtpClient.Send(msg);

.....

After you only have to do a web page like resetPassword.aspx and handle in the page load the parameters "guid" which it should be link to the user in the dataBase