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

时间:2011-09-05 16:40:46

标签: c# sql-server passwordbox securestring

我有一个登录窗口,可以从用户那里获取用户名和密码,我想知道处理密码的最佳方法。用户名只是常规文本框,但密码是PasswordBox。我将用户名直接传递给ViewModel,但只有在使用Code-Behind单击Login按钮后才在ViewModel上设置SecureString属性。设置密码安全串后,我想验证。

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

2 个答案:

答案 0 :(得分:4)

你永远不应该存储密码 - 甚至不加密......

只需存储密码的哈希值(只要哈希以安全的方式实现,就可以阻止密码被检索),并且对于验证,您可以用相同的方式对用户提供的密码进行哈希处理并比较结果...

有标准可以这样做:

上述标准使得很难使用彩虹表等,因为它使得计算非常昂贵,因为除了盐之外它还使用了几轮...因此散列例如慢1000倍(1000轮)但是这样正是你想要的 - 攻击者需要做同样的计算,因此需要1000倍的进动力或时间才能通过蛮力实现目标...

您可以将结果直接存储为VARBINARY,也可以在Base64-或HEX编码字节后将其存储为VARCHAR ...您需要将盐与其一起存储(只要每个密码都获得它,就没有安全风险)拥有独特的加密安全生成的随机盐)。

答案 1 :(得分:0)

在我之前的演出中,我们将密码存储为散列/加密/加盐值(当时使用MD5)作为VARBINARY(32)。为了稍后比较密码,我们将比较我们存储的加密+盐渍值与尝试密码的加密+盐渍值,而不是尝试解密密码。如果他们匹配,他们进来,如果他们不匹配,他们没有进入。

哈希工作是在中间层完成的(两者都是为了最初保存密码和以后进行比较),但是基于SQL Server的例子(停止@Yahia的抱怨,这并不是要告诉你最安全的方式,我只是用一个非常轻量级的例子来说明方法.MD5对你来说还不够强大?你可以使用不同的,更复杂的算法以及更高级的salting技术,特别是如果你在应用程序层中执行散列):

CREATE TABLE dbo.Users
(
    UserID INT IDENTITY(1,1) PRIMARY KEY,
    Username NVARCHAR(255) NOT NULL UNIQUE,
    PasswordHash VARBINARY(32) NOT NULL
);

创建用户的过程(无错误处理或欺骗预防,只是伪)。

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

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

    INSERT dbo.Users(Username, Password)
        SELECT @Username, 
          CONVERT(VARBINARY(32), HASHBYTES('MD5', @Password + @Salt));
END
GO

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

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,而是从其他地方检索它。

这肯定比存储未加密的密码更安全,但它删除了检索密码的能力。在我看来是双赢的。