views:

779

answers:

2

Does anyone has 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.

A: 

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
Downvoted for the abuse of Thread.Sleep in the code sample - if you want truly random data, use the crypto rng providers.
Neal
There, removed it. Once you allow for race conditions, you realize there is no 'true' random data.
Ryk
Have a look at the crypto rngs that .net provides ...
Neal
Will follow Neal's advice, thank you for your help anyway.
Rui Santos
+1  A: 

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

Jean-Christophe Fortin