【问题标题】:How to create a asp.net membership provider hashed password manually?如何手动创建 asp.net 会员提供者散列密码?
【发布时间】:2010-04-01 04:18:46
【问题描述】:

我使用网站作为前端,所有用户都通过标准 ASP.NET Membership-Provider 进行身份验证。密码以“散列”方式保存在 SQL 数据库中。

现在我想编写一个具有管理功能的桌面客户端。除其他外,应该有一种方法来重置用户密码。我可以使用保存的会员数据访问数据库,但是如何手动创建密码盐和哈希?使用 System.Web.Membership 命名空间似乎不合适,所以我需要知道如何手动创建新密码的 salt 和 hash。

专家站出来! :)

【问题讨论】:

  • 为什么 System.Web.Security 不合适?似乎是适合这项工作的工具。
  • 我不能使用我的 sql 数据库在桌面客户端应用程序中包含会员提供程序,可以吗?如果有可能你是对的,我可以使用这些方法……但是如何实现呢?

标签: asp.net asp.net-membership hash password-protection salt


【解决方案1】:

我使用反射器来查看 .NET-Framework 内部使用的那些方法。也许有可用的公共方法,但我没有找到它们 - 如果您知道如何以用户身份查询这些内部方法,请留下评论! :)

这是简化的源代码,没有不必要的条件,因为我只想将密码编码为 SHA1-Hash:

private string GenerateSalt() {
  var buf = new byte[16];
  (new RNGCryptoServiceProvider()).GetBytes(buf);
  return Convert.ToBase64String(buf);
}

private string EncodePassword(string pass, string salt) {
    byte[] bytes = Encoding.Unicode.GetBytes(pass);
    byte[] src = Convert.FromBase64String(salt);
    byte[] dst = new byte[src.Length + bytes.Length];
    byte[] inArray = null;
    Buffer.BlockCopy(src, 0, dst, 0, src.Length);
    Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
    HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
    inArray = algorithm.ComputeHash(dst);
    return Convert.ToBase64String(inArray);
}

【讨论】:

  • 无需使用 Reflector,默认提供程序的源代码很容易获得 - download.microsoft.com/download/a/b/3/… 。看一下SqlMembershipProvider的EncodePassword方法实现。它使用默认哈希算法,除非为 MembershipProvider 指定了一种。 IIRC,默认为 SHA-1
【解决方案2】:

您绝对可以在控制台或 winforms 应用程序中使用 System.Web.Security。

这是一个简单的控制台应用程序:

static void Main(string[] args)
{
    MembershipProvider provider = Membership.Provider;

    MembershipUser myUser = provider.GetUser("myUser", false);

    if( myUser != null ) provider.DeleteUser("myUser", true);

    MembershipCreateStatus status;

    myUser = provider.CreateUser("myUser", "password", "user@example.com", null, null, true, null, out status);

    if (status != MembershipCreateStatus.Success)
    {
        Console.WriteLine("Could not create user.  Reason: " + status.ToString());
        Console.ReadLine();
        return;
    }

    Console.WriteLine("Authenticating with \"password\": " + provider.ValidateUser("myUser", "password").ToString());

    string newPassword = myUser.ResetPassword();

    Console.WriteLine("Authenticating with \"password\": " + provider.ValidateUser("myUser", "password").ToString());
    Console.WriteLine("Authenticating with new password: " + provider.ValidateUser("myUser", newPassword).ToString());

    Console.ReadLine();
}

还有 app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
        <add name="MyConnectionString" connectionString="Data Source=localhost;Initial Catalog=MyDatabase;Integrated Security=True" providerName="System.Data.SqlClient" />
    </connectionStrings>
    <system.web>
        <membership defaultProvider="MyMembershipProvider">
            <providers>
                <clear />
                <add name="MyMembershipProvider"
                     type="System.Web.Security.SqlMembershipProvider"
                     connectionStringName="MyConnectionString"
                     applicationName="MyApplication"
                     minRequiredPasswordLength="5"
                     minRequiredNonalphanumericCharacters="0"
                     requiresQuestionAndAnswer="false" />
            </providers>
        </membership>
    </system.web>
</configuration>

【讨论】:

    【解决方案3】:

    快速脏方法

    Public Shared Function GetSaltKey() As String
                Dim saltBytes() As Byte
                Dim minSaltSize As Integer = 4
                Dim maxSaltSize As Integer = 8
    
                ' Generate a random number for the size of the salt.
                Dim random As Random
                random = New Random()
    
                Dim saltSize As Integer
                saltSize = random.Next(minSaltSize, maxSaltSize)
    
                ' Allocate a byte array, which will hold the salt.
                saltBytes = New Byte(saltSize - 1) {}
    
                ' Initialize a random number generator.
                Dim rng As RNGCryptoServiceProvider
                rng = New RNGCryptoServiceProvider()
    
                ' Fill the salt with cryptographically strong byte values.
                rng.GetNonZeroBytes(saltBytes)
    
                ' Convert plain text into a byte array.
                Return Convert.ToBase64String(saltBytes)
            End Function
    
            Public Shared Function ComputeHash(ByVal password As String, ByVal salt As String) As String
    
                Return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(salt & password, _
                    System.Web.Configuration.FormsAuthPasswordFormat.SHA1.ToString)
            End Function
    

    虽然,成员命名空间也为此内置了一些东西,如被遗忘的分号所述

    【讨论】:

    • 不错的方法,但与标准的 asp.net 会员提供者使用的方法不同 - 使用此方法设置新密码后,用户无法再登录。无论如何谢谢:)
    • 我在生产站点(旧站点)上将此代码与 asp.net 会员提供程序一起使用,您重新计算哈希和盐密钥并比较每个身份验证,这是派生安全性的地方从。不要挑剔,但你的问题只问如何计算盐和手动键。
    【解决方案4】:

    自从我修改 ASP.Net 成员资格以来已经有一段时间了,但请记住处理一些与它有点相关的事情(由于现有的用户数据库需要自定义事情)。在这项工作中,我覆盖了这些方法(现有用户 db 有 md5 散列密码)。

    所以在同一个“思路”中:

    通过您的桌面应用可以引用的 Web 服务公开 Membership API。这样,您不是在“重新创建”事物,而是在重新使用它们。您不必覆盖任何内容,您只需通过 Web 服务为您的桌面应用程序公开现有方法。

    不用说你必须保护这个端点....

    如果以上内容对您的口味来说过于粗略,这里有一个指向 asp.net 论坛的链接,其中涉及一些尝试重新创建散列的尝试......我无法确认准确性,但应该很容易测试出来:

    http://forums.asp.net/p/1336657/2899172.aspx

    【讨论】:

    • 感谢您的网络服务理念。你说得对,在正常情况下,我会通过服务提供整个 Membership-Thing。不幸的是,公司政策不允许这种网络服务:(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 2011-11-10
    • 2011-04-09
    • 2018-03-16
    • 2011-02-23
    • 2016-06-06
    相关资源
    最近更新 更多