简体   繁体   English

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

[英]Store, retrieve and validate password (SecureString) in SQL Server

I have a login window that gets the username and password from the user and I would like to know the best way to handle the password. 我有一个登录窗口,从用户获取用户名和密码,我想知道处理密码的最佳方法。 The username is just a regular textbox, but the password is a PasswordBox. 用户名只是常规文本框,但密码是PasswordBox。 I pass the username directly to the ViewModel, but only set a SecureString property on the ViewModel once the Login button is clicked using Code-Behind. 我将用户名直接传递给ViewModel,但只有在使用Code-Behind单击Login按钮后才在ViewModel上设置SecureString属性。 After the Password SecureString is set I want to Validate. 设置密码安全串后,我想验证。

I'm writing the LoginBox now, but I don't have the Model fully worked out yet. 我现在正在编写LoginBox,但我还没有完整的模型。 How should I store the password in SQL Server? 我该如何在SQL Server中存储密码? Do I just write the contents of the SecureString to SQL and then try to compare it when the user tries to login? 我只是将SecureString的内容写入SQL,然后在用户尝试登录时尝试比较它吗?

You should NEVER store a password - not even encrypted... 你永远不应该存储密码 - 甚至不加密...

Just store a hash of the password (which prevents the pasword from being ever retrieved as long as the hashing is implemented in a secure manner) and for validation you hash the user supplied password the same way and compare the results... 只需存储密码的哈希值(只要哈希以安全的方式实现,就可以防止密码被检索),并且对于验证,您可以用相同的方式对用户提供的密码进行哈希处理并比较结果...

There are standards to do so: 有这样的标准:

The above standard makes it hard to use rainbow tables etc. because it makes the calculation very expensive since it uses several rounds in addition to a salt... thus hashing is for example 1000 times slower (with 1000 rounds) but this is exactly what you want - the attacker will need to do the same calculation and thus will need 1000 times the precessing power or time to achieve the goal by brute force... 上面的标准使得很难使用彩虹表等,因为它使得计算非常昂贵,因为除了盐之外它还使用了几轮...因此散列例如慢1000倍(1000轮)但这正是你想要的 - 攻击者需要做同样的计算,因此需要1000倍的进动力或时间才能通过蛮力实现目标......

You can store the result either as VARBINARY directly or as VARCHAR after Base64- or HEX-encoding the bytes... you will need to store the salt along with it (which is no security risk as long as every password gets its own distinct cryptographically secure generated random salt). 您可以将结果直接存储为VARBINARY,也可以在Base64-或HEX编码字节后将其存储为VARCHAR ...您需要将盐与其一起存储(只要每个密码都有自己独特的加密方式,这就没有安全风险安全生成随机盐)。

At my previous gig we stored the password as a hashed/encrypted/salted value (using MD5 at the time) as VARBINARY(32) . 在我之前的演出中,我们将密码存储为散列/加密/加盐值(当时使用MD5)作为VARBINARY(32) To compare the password later, rather than try to decrypt the password, we would compared the encrypted + salted value that we stored to the encrypted + salted value of the password being attempted. 为了稍后比较密码,我们将比较我们存储的加密+盐渍值与尝试密码的加密+盐渍值,而不是尝试解密密码。 If they matched, they got in, if they didn't match, they didn't get in. 如果他们匹配,他们进来,如果他们不匹配,他们没有进入。

The hashing work was done in the middle tier (both for saving the password initially and for comparing later), but a SQL Server-based example ( to stop @Yahia's grumbling, this is not meant to tell you the most secure way possible, I am just illustrating the methodology with a very lightweight example. MD5 not strong enough for you? You can use a different and more complex algorithm along with more advanced salting techniques, especially if you perform the hashing in the application tier ): 散列工作是在中间层完成的(最初都是为了保存密码和以后进行比较),但是基于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
);

A procedure to create a user (no error handling or dupe prevention, just 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

Now a procedure to authenticate a user. 现在是验证用户身份的过程。

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

In reality you would likely perform the hashing within the application, and pass the hash values in as VARBINARY(32) - this makes it much harder to "sniff" the actual clear-text password from anywhere. 实际上,您可能会在应用程序中执行散列,并将散列值作为VARBINARY(32)传递 - 这使得从任何地方“嗅探”实际的明文密码变得更加困难。 And you maybe wouldn't store the salt in plain text with the code either, but retrieve it from elsewhere. 并且您可能不会使用代码以纯文本形式存储salt,而是从其他地方检索它。

This is definitely more secure than storing the password unencrypted, but it removes the ability to ever retrieve the password. 这肯定比存储未加密的密码更安全,但它删除了检索密码的能力。 Win-win in my opinion. 在我看来双赢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM