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

本文关键字:SecureString SQL Server 密码 检索 验证 存储 | 更新日期: 2023-09-27 18:07:44

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

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

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

永远不要存储密码-甚至不要加密…

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



  • http://en.wikipedia.org/wiki/PBKDF2
  • http://www.ietf.org/rfc/rfc2898.txt
  • http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

上述标准使得很难使用彩虹表等,因为它使得计算非常昂贵,因为它除了使用盐之外还使用了几轮…因此,例如,哈希要慢1000倍(1000轮),但这正是你想要的——攻击者需要做同样的计算,因此需要1000倍的处理能力或时间来通过暴力破解实现目标…

您可以将结果直接存储为VARBINARY或在Base64或hex编码后存储为VARCHAR…您需要将盐与它一起存储(只要每个密码都有自己独特的加密安全生成的随机盐,就没有安全风险)。

在我之前的工作中,我们将密码存储为散列/加密/加盐值(当时使用MD5)作为VARBINARY(32)。为了稍后比较密码,而不是尝试解密密码,我们将把存储的加密+加盐值与正在尝试的密码的加密+加盐值进行比较。如果他们匹配,他们就被录取,如果他们不匹配,他们就不被录取。

散列工作是在中间层完成的(既用于最初保存密码,也用于稍后进行比较),但是一个基于SQL server的示例(以阻止@Yahia的抱怨),这并不是要告诉您最安全的方法,我只是用一个非常轻量级的示例来说明该方法。MD5对你来说不够强?您可以使用不同的更复杂的算法以及更高级的盐化技术,特别是如果您在应用程序层执行散列):

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

创建一个用户的过程(没有错误处理或防止欺骗,只有pseudo)。

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以纯文本的形式存储在代码中,而是从其他地方检索它。

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