C# 在SQL Server中存储、检索和验证密码(SecureString)

C# 在SQL Server中存储、检索和验证密码(SecureString),c#,sql-server,passwordbox,securestring,C#,Sql Server,Passwordbox,Securestring,我有一个登录窗口,从用户那里获取用户名和密码,我想知道处理密码的最佳方法。用户名只是一个普通的文本框,但密码是一个密码框。我将用户名直接传递给ViewModel,但仅在使用代码隐藏单击登录按钮后才在ViewModel上设置SecureString属性。设置密码SecureString后,我要验证 我现在正在写LoginBox,但是我还没有完全建立模型。如何在SQL Server中存储密码?我是否只是将SecureString的内容写入SQL,然后在用户尝试登录时尝试进行比较?在我之前的gig中,

我有一个登录窗口,从用户那里获取用户名和密码,我想知道处理密码的最佳方法。用户名只是一个普通的文本框,但密码是一个密码框。我将用户名直接传递给ViewModel,但仅在使用代码隐藏单击登录按钮后才在ViewModel上设置SecureString属性。设置密码SecureString后,我要验证


我现在正在写LoginBox,但是我还没有完全建立模型。如何在SQL Server中存储密码?我是否只是将SecureString的内容写入SQL,然后在用户尝试登录时尝试进行比较?

在我之前的gig中,我们将密码存储为哈希/加密/盐析值(当时使用MD5)作为
VARBINARY(32)
。为了稍后比较密码,而不是尝试解密密码,我们会将存储的加密+加盐值与尝试的密码的加密+加盐值进行比较。如果他们匹配,他们就进入,如果他们不匹配,他们就不会进入

散列工作是在中间层完成的(既用于初始保存密码,也用于稍后比较),而是基于SQLServer的示例(为了停止@Yahia的抱怨,这并不是为了告诉你可能的最安全的方法,我只是用一个非常轻量级的例子来说明该方法。MD5对你来说不够强大?你可以使用不同的、更复杂的算法以及更先进的盐析技术,特别是如果你在应用程序中执行哈希运算离子层):

创建用户的过程(没有错误处理或重复防止,只是伪操作)

现在是验证用户身份的过程

CREATE PROCEDURE dbo.User_Authenticate
    @Username NVARCHAR(255),
    @Password NVARCHAR(16)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @salt NVARCHAR(16) = '$w0rdf1$h';

    IF EXISTS 
    (
      SELECT 1 FROM dbo.Users
        WHERE Username = @Username AND 
        PasswordHash = CONVERT(VARBINARY(32), HASHBYTES('MD5', @Password + @salt))
    )
    BEGIN
        PRINT 'Please, come on in!';
    END
    ELSE
    BEGIN
        PRINT 'You can keep knocking but you cannot come in.';
    END
END
GO
实际上,您可能会在应用程序中执行散列,并将散列值作为VARBINARY(32)传递-这使得从任何地方“嗅探”实际的明文密码变得更加困难。而且您可能也不会将salt与代码一起存储在纯文本中,而是从其他地方检索它


这肯定比存储未加密的密码更安全,但它会消除检索密码的能力。我认为这是双赢。

在我之前的工作中,我们将密码存储为哈希/加密/盐析值(当时使用MD5)作为
VARBINARY(32)
。为了稍后比较密码,而不是尝试解密密码,我们将存储的加密+加密值与尝试使用的密码的加密+加密值进行比较。如果匹配,则进入,如果不匹配,则不进入

散列工作是在中间层完成的(既用于初始保存密码,也用于稍后比较),而是基于SQLServer的示例(为了停止@Yahia的抱怨,这并不是为了告诉你可能的最安全的方法,我只是用一个非常轻量级的例子来说明该方法。MD5对你来说不够强大?你可以使用不同的、更复杂的算法以及更先进的盐析技术,特别是如果你在应用程序中执行哈希运算离子层):

创建用户的过程(没有错误处理或重复防止,只是伪操作)

现在是验证用户身份的过程

CREATE PROCEDURE dbo.User_Authenticate
    @Username NVARCHAR(255),
    @Password NVARCHAR(16)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @salt NVARCHAR(16) = '$w0rdf1$h';

    IF EXISTS 
    (
      SELECT 1 FROM dbo.Users
        WHERE Username = @Username AND 
        PasswordHash = CONVERT(VARBINARY(32), HASHBYTES('MD5', @Password + @salt))
    )
    BEGIN
        PRINT 'Please, come on in!';
    END
    ELSE
    BEGIN
        PRINT 'You can keep knocking but you cannot come in.';
    END
END
GO
实际上,您可能会在应用程序中执行散列,并将散列值作为VARBINARY(32)传递-这使得从任何地方“嗅探”实际的明文密码变得更加困难。而且您可能也不会将salt与代码一起存储在纯文本中,而是从其他地方检索它


这肯定比未加密的密码更安全,但它会删除检索密码的功能。在我看来,Win Win Win。

您不应该存储密码-甚至不加密

只需存储密码的散列(只要以安全的方式实现散列,就可以防止检索pasword),对于验证,您可以使用相同的方式对用户提供的密码进行散列,并比较结果

有这样做的标准:

上述标准使得使用rainbow表等变得很困难,因为它使得计算非常昂贵,因为它除了使用一个salt外还使用了几轮……因此哈希运算的速度要慢1000倍(1000轮)但这正是你想要的-攻击者将需要做同样的计算,因此需要1000倍的进动能力或时间来通过暴力达到目标


您可以直接将结果存储为VARBINARY,也可以在Base64或十六进制编码字节后存储为VARCHAR……您需要将salt与之一起存储(只要每个密码都有自己独特的加密安全生成的随机salt,就没有安全风险).

您不应该存储密码-甚至不加密

只需存储密码的散列(只要以安全的方式实现散列,就可以防止检索pasword),对于验证,您可以使用相同的方式对用户提供的密码进行散列,并比较结果

有这样做的标准:

上述标准使得使用rainbow表等变得很困难,因为它使得计算非常昂贵,因为它除了使用一个salt外还使用了几轮……因此哈希运算的速度要慢1000倍(1000轮)但这正是你想要的-攻击者将需要做同样的计算,因此需要1000倍的进动能力或时间来通过暴力达到目标

您可以直接将结果存储为VARBINARY,也可以在Base64或十六进制编码字节后存储为VARCHAR……您需要将salt与之一起存储(只要每个密码都有自己独特的加密安全生成的rando,这就没有安全风险)
CREATE PROCEDURE dbo.User_Authenticate
    @Username NVARCHAR(255),
    @Password NVARCHAR(16)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @salt NVARCHAR(16) = '$w0rdf1$h';

    IF EXISTS 
    (
      SELECT 1 FROM dbo.Users
        WHERE Username = @Username AND 
        PasswordHash = CONVERT(VARBINARY(32), HASHBYTES('MD5', @Password + @salt))
    )
    BEGIN
        PRINT 'Please, come on in!';
    END
    ELSE
    BEGIN
        PRINT 'You can keep knocking but you cannot come in.';
    END
END
GO