Членство в ASP.Net сохраняет измененный пароль в виде обычного текста даже с установленным хэшированным паролем. - PullRequest
2 голосов
/ 09 ноября 2009

Я использую ASP.Net SqlMembershipProvider для управления моими пользователями. Вот мой конфиг:

<membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
            <providers>
                <clear />
                <add
                    name="SqlProvider"
                    type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
                    connectionStringName="SiteDatabase"
                    applicationName="WPR"
                    minRequiredPasswordLength="6"
                    minRequiredNonalphanumericCharacters="0"
                    enablePasswordRetrieval="false"
                    enablePasswordReset="true"
                    requiresQuestionAndAnswer="false"
                    requiresUniqueEmail="true"
                    passwordFormat="Hashed" />
            </providers>
        </membership>

Моя проблема заключается в следующем: когда я вызываю Membership.CreateUser для создания новых пользователей, пароль хранится в БД в хешированном формате с солью - и это все хорошо. Однако, когда я вызываю Membership.ChangePassword в функции администратора, он сохраняет пароль в текстовом формате. Я действительно не могу понять это поведение, так как в конфигурации четко написано «Хэширование», а создание нового пользователя создает хэшированный пароль.

Ответы [ 3 ]

6 голосов
/ 09 ноября 2009

В методе ChangePassword() поставщика ASPMembership по умолчанию формат пароля для существующего пользователя извлекается из базы данных и представляет собой формат, используемый для кодирования нового пароля для существующего пользователя, а не формат пароля, который установлен в web.config, который теперь может указывать другой формат для использования. Вы можете убедиться в этом сами, загрузив исходный код для поставщиков по умолчанию .

Тогда мой вопрос: хранится ли пароль в виде открытого текста для пользователя, у которого уже был пароль, хранящийся в виде открытого текста? Это легко проверить, проверив значение поля PasswordFormat для пользователя в таблице aspnet_Membership. Значения:

Clear = 0,
Hashed = 1,
Encrypted = 2,

РЕДАКТИРОВАТЬ:

если вам нужно самостоятельно хэшировать пароли, код инфраструктуры может пригодиться

// generate a salt
public string GenerateSalt()
{
    byte[] buf = new byte[16];
    (new RNGCryptoServiceProvider()).GetBytes(buf);
    return Convert.ToBase64String(buf);
}

// hashes the password, using the supplied salt
public string HashPassword(string pass, string salt)
{
    byte[] bIn = Encoding.Unicode.GetBytes(pass);
    byte[] bSalt = Convert.FromBase64String(salt);
    byte[] bAll = new byte[bSalt.Length + bIn.Length];
    byte[] bRet = null;

    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);

    // this assumes a Hashed password (PasswordFormat = 1)
    HashAlgorithm s = HashAlgorithm.Create( Membership.HashAlgorithmType );
    bRet = s.ComputeHash(bAll);

    return Convert.ToBase64String(bRet);
}

теперь вам просто нужно извлечь все записи из базы данных, где PasswordFormat = 0, запустить их через консольное приложение, чтобы хэшировать пароль и сохранить соль, хешированный пароль в базе данных, а также обновить поле PasswordFormat до 1

1 голос
/ 09 сентября 2014

Для этого я использую консольное приложение. Прямо в базе данных меняю PasswordFormat в таблице aspnet_Membership. Затем я меняю пароль на тот же с помощью методов ResetPassword и ChangePassword. Также, если пользователь был заблокирован, я разблокирую его перед сменой пароля, а затем снова блокирую. В файле app.config у меня есть строки подключения для модели базы данных и поставщика членства, а также определение поставщика членства.

Обратите внимание, что если старый пароль не соответствует вашим последним требованиям к паролю в настройках провайдера, то метод ChangePassword завершится неудачей.

Код следующий:

static void Main(string[] args)
{
    using (var db = new MyEntities())
    {
        var usersToFix = db.aspnet_Membership.Where(x => x.PasswordFormat == 0).ToList();

        foreach (var userToFix in usersToFix)
        {
            userToFix.PasswordFormat = 1;

            var password = userToFix.Password;
            var passwordQuestion = userToFix.PasswordQuestion;
            var passwordAnswer = userToFix.PasswordAnswer;
            var lastLockoutDate = userToFix.LastLockoutDate;

            db.SaveChanges();


            var user = Membership.GetUser(userToFix.UserId);
            bool locked = user.IsLockedOut;

            if (locked)
            {
                user.UnlockUser();
            }

            var resetPassword = user.ResetPassword();
            user.ChangePassword(resetPassword, password);
            user.ChangePasswordQuestionAndAnswer(password, passwordQuestion, passwordAnswer);

            if (locked)
            {
                userToFix.IsLockedOut = true;
                userToFix.LastLockoutDate = lastLockoutDate;
                db.SaveChanges();
            }

            Console.WriteLine("{0} - OK", user.UserName);
        }
    }


    Console.WriteLine("Done!");
    Console.ReadKey();
}
0 голосов
/ 15 марта 2012

Решение Русса, вероятно, работает, но есть более простой способ, если все ваши существующие пользователи имеют либо открытые, либо зашифрованные пароли. Установите в своем web.config 2 провайдера членства в sql, один из которых использует чистые (или зашифрованные) пароли, а другой - хэшированные. Затем выполните этот код где-нибудь в вашем веб-приложении:

void ConvertPasswordsToHashed()
{
    var clearProvider = Membership.Providers["SqlProvider"];
    var hashedProvider = Membership.Providers["SqlProvider_Hashed"];
    int dontCare;
    if (clearProvider == null || hashedProvider == null) return;
    var passwords = clearProvider.GetAllUsers(0, int.MaxValue, out dontCare)
        .Cast<MembershipUser>().ToDictionary(u => u.UserName, u => u.GetPassword());

    using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ConnectionString))
    {
        conn.Open();
        using (var cmd = new SqlCommand("UPDATE [aspnet_Membership] SET [PasswordFormat]=1", conn))
            cmd.ExecuteNonQuery();
    }

    foreach (var entry in passwords)
    {
        var resetPassword = hashedProvider.ResetPassword(entry.Key, null);
        hashedProvider.ChangePassword(entry.Key, resetPassword, entry.Value);
    }
}
...