Strange PasswordRecovery behavior?

G

Guest

I have a PasswordRecovery web control as part of my login functionality that
I have successfully tested in an application I'm building. I was asked to
migrate some historical user data into the aspnet_application_services tables
for this app, so I thought the best thing to do would be to just migrate all
the user data with the exception of the passwords, and add text to the
membership comment that read 'EncryptMe' or something like that. Then just
write a method that would check the comments, and if it said the same thing
(encryptme) it would reset the password using the membership object
ResetPassword method. Ok so all this works like a charm, until I try to do
the password recovery as if I was one of these historical users logging in
and trying to find out my new password that was generated. The email arrives
without a hitch, except...only HALF the password shows up. I confirmed this
because I output the newly generated passwords when I run the reset password
method, and then compared it to the one in the email. The last example
looked like this:

Generated password: BvCj@y-b88KO{U
Emailed password: 88KO{U

I'm stumped. Any ideas? I did notice that there's no longer a password
salt in the membership database, could this be the problem? Any idea how to
deal with it if it is?

Help!!

TIA,

Morgan
Web Developer
Portland, Oregon
 
W

Walter Wang [MSFT]

Hi Morgan,

That's indeed a very strange issue. If you handle the PasswordRecovery's
SendingMail event and check MailMessageEventArgs.Message.Body, does it
already contain truncated password?

The default Membership provider should have already enabled resetting
password when recoverying password; what if you comment the call to
ResetPassword and use PasswordRecovery directly?

If you create a small test web application from scratch and test this
PasswordRecovery function, does it work?


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

I checked the MailMessageEventArgs.Message.Body, and it was truncated already
at that point. So I changed the web config to not allow password retrieval
and allow password reset, and that worked. Thanks for the recommendation!
It's not the solution I was hoping for, but it is perfectly workable.

I am still curious as to why the passwords were being truncated in the first
place, and only on the historical data? I checked a non-historical user, and
the password retrieval worked as expected. I also double checked the
truncation effect for historical entries, and was able to consistently repeat
it. My only guess remains that it has to do with the password salt column,
since that is populated on non historical data and empty for historical
entries. Any ideas? Insight into how the salt works?
 
W

Walter Wang [MSFT]

Hi Morgan,

The password salt is used to further protect the password from attaching by
using a random string to prefix the password before hashing it:

#Security Briefs: Hashing Passwords, The AllowPartiallyTrustedCallers
Attribute -- MSDN Magazine, August 2003
http://msdn.microsoft.com/msdnmag/issues/03/08/SecurityBriefs/
<quote>
To slow down the attack, use salt. Salt is a way to season the passwords
before hashing them, making the attacker's precomputed dictionary useless.
Here's how it's done. Whenever you add an entry to the database, you
calculate a random string of digits to be used as salt. When you want to
calculate the hash of Alice's password, you look up the salt value for
Alice's account, prepend it to the password, and hash them together.
</quote>

You can also use Reflector (http://www.aisto.com/roeder/dotnet/) to view
MembershipProvider.EncodePassword:

internal string EncodePassword(string pass, int passwordFormat, string
salt)
{
if (passwordFormat == 0)
{
return pass;
}
byte[] buffer1 = Encoding.Unicode.GetBytes(pass);
byte[] buffer2 = Convert.FromBase64String(salt);
byte[] buffer3 = new byte[buffer2.Length + buffer1.Length];
byte[] buffer4 = null;
Buffer.BlockCopy(buffer2, 0, buffer3, 0, buffer2.Length);
Buffer.BlockCopy(buffer1, 0, buffer3, buffer2.Length,
buffer1.Length);
if (passwordFormat == 1)
{
HashAlgorithm algorithm1 =
HashAlgorithm.Create(Membership.HashAlgorithmType);
if ((algorithm1 == null) &&
Membership.IsHashAlgorithmFromMembershipConfig)
{

RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
}
buffer4 = algorithm1.ComputeHash(buffer3);
}
else
{
buffer4 = this.EncryptPassword(buffer3);
}
return Convert.ToBase64String(buffer4);
}


Based on my understanding, ResetPassword should also be using this method
to reset the password. I'm not sure why it will include a password salt
while the database actually doesn't have one. Maybe you could post some
code here to let me reproduce the issue on my side and find out why.

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Here's some code and data for you to try working with:

Password:
hk4aoIj2cSjpcGtDIqnxt78ao71pnIyLbTPH+Tv3KR4=
MMlZVIjED94iuCadeB9nNhiqYW1s28t3a2s83OIbjaE=


PasswordFormat:
2
2

PasswordSalt:
(EMPTY STRING)
Jm/fuPDVPA22OYVC0WJ95A==

Comment:
"EncryptMe"
(NULL)

Code:
foreach (MembershipUser theUser in allUsers)
{
if (theUser.Comment == "EncryptMe")
{
p = theUser.ResetPassword();
theUser.Comment = "";
Membership.UpdateUser(theUser);
}
}



Insert the two records into aspnet_membership (fill in the rest of the
columns with whatever defaults or data you like)

Run the code, it should generate a new password for the record w/ no salt
(You can add the 'encryptme' to the other record too, so you know what the
passwords are for both)

try to retrieve both passwords. One should work, one should not.

--
Morgan
Web Developer
Portland, Oregon


"Walter Wang [MSFT]" said:
Hi Morgan,

The password salt is used to further protect the password from attaching by
using a random string to prefix the password before hashing it:

#Security Briefs: Hashing Passwords, The AllowPartiallyTrustedCallers
Attribute -- MSDN Magazine, August 2003
http://msdn.microsoft.com/msdnmag/issues/03/08/SecurityBriefs/
<quote>
To slow down the attack, use salt. Salt is a way to season the passwords
before hashing them, making the attacker's precomputed dictionary useless.
Here's how it's done. Whenever you add an entry to the database, you
calculate a random string of digits to be used as salt. When you want to
calculate the hash of Alice's password, you look up the salt value for
Alice's account, prepend it to the password, and hash them together.
</quote>

You can also use Reflector (http://www.aisto.com/roeder/dotnet/) to view
MembershipProvider.EncodePassword:

internal string EncodePassword(string pass, int passwordFormat, string
salt)
{
if (passwordFormat == 0)
{
return pass;
}
byte[] buffer1 = Encoding.Unicode.GetBytes(pass);
byte[] buffer2 = Convert.FromBase64String(salt);
byte[] buffer3 = new byte[buffer2.Length + buffer1.Length];
byte[] buffer4 = null;
Buffer.BlockCopy(buffer2, 0, buffer3, 0, buffer2.Length);
Buffer.BlockCopy(buffer1, 0, buffer3, buffer2.Length,
buffer1.Length);
if (passwordFormat == 1)
{
HashAlgorithm algorithm1 =
HashAlgorithm.Create(Membership.HashAlgorithmType);
if ((algorithm1 == null) &&
Membership.IsHashAlgorithmFromMembershipConfig)
{

RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
}
buffer4 = algorithm1.ComputeHash(buffer3);
}
else
{
buffer4 = this.EncryptPassword(buffer3);
}
return Convert.ToBase64String(buffer4);
}


Based on my understanding, ResetPassword should also be using this method
to reset the password. I'm not sure why it will include a password salt
while the database actually doesn't have one. Maybe you could post some
code here to let me reproduce the issue on my side and find out why.

Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
W

Walter Wang [MSFT]

Hi Morgan,

Thanks for your detailed code. I've been able to reproduce the issue on my
side.

Further research shows the PasswordSalt column is essential for
SqlMembershipProvider to retrieve the password. Usually this column is
maintained internally by the provider and you don't need to handle it.
Since your scenario is related to some legacy data and need to migrate to
it, I suggest use following function to generate a PasswordSalt value for
each user which currently doesn't have one:

string GenerateSalt()
{
byte[] data = new byte[0x10];
new RNGCryptoServiceProvider().GetBytes(data);
return Convert.ToBase64String(data);
}


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top