我有一个登录窗口,从用户获得用户名和密码,我想知道处理密码的最佳方法。用户名只是一个普通的文本框,但密码是一个PasswordBox。我将用户名直接传递给ViewModel,但只有在使用Code-Behind单击Login按钮后才在ViewModel上设置SecureString属性。设置Password SecureString后,我要验证。
我现在正在写LoginBox,但是我还没有完全解决这个模型。如何在SQL Server中保存密码?我只是写SecureString的内容到SQL,然后尝试比较它时,用户试图登录?
永远不要存储密码-甚至不要加密…
只需存储密码的散列(只要以安全的方式实现散列,就可以防止密码被检索),并且对于验证,您以相同的方式对用户提供的密码进行散列并比较结果…
- 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以纯文本的形式存储在代码中,而是从其他地方检索它。
这绝对比存储未加密的密码更安全,但它消除了检索密码的能力。我认为这是双赢。