17

我在 sql server 2000 中有这个查询:

select pwdencrypt('AAAA')

它输出一个加密的“AAAA”字符串:

0x0100CF465B7B12625EF019E157120D58DD46569AC7BF4118455D12625EF019E157120D58DD46569AC7BF4118455D

如何转换(解密)来自其来源(即“AAAA”)的输出?

4

6 回答 6

24

SQL Server 密码哈希算法:

hashBytes = 0x0100 | fourByteSalt | SHA1(utf16EncodedPassword+fourByteSalt)

例如,散列密码“正确的马电池订书钉”。首先我们生成一些随机盐:

fourByteSalt = 0x9A664D79;

然后将密码(以 UTF-16 编码)与盐一起散列:

 SHA1("correct horse battery staple" + 0x9A66D79);
=SHA1(0x63006F007200720065006300740020006200610074007400650072007900200068006F00720073006500200073007400610070006C006500 0x9A66D79)
=0x6EDB2FA35E3B8FAB4DBA2FFB62F5426B67FE54A3

存储在syslogins表中的值是以下内容的串联:

[标题] + [盐] + [哈希]
0x0100 9A664D79 6EDB2FA35E3B8FAB4DBA2FFB62F5426B67FE54A3

您可以在 SQL Server 中看到:

SELECT 
   name, CAST(password AS varbinary(max)) AS PasswordHash
FROM sys.syslogins
WHERE name = 'sa'

name  PasswordHash
====  ======================================================
sa    0x01009A664D796EDB2FA35E3B8FAB4DBA2FFB62F5426B67FE54A3
  • 版本标题:0100
  • 盐(四个字节):9A664D79
  • 哈希:6EDB2FA35E3B8FAB4DBA2FFB62F5426B67FE54A3 (SHA-1 为 20 字节;160 位)

验证

您通过执行相同的哈希来验证密码:

  • 从保存中获取盐PasswordHash:0x9A664D79

并再次执行哈希:

SHA1("correct horse battery staple" + 0x9A66D79);

这将得出相同的哈希值,并且您知道密码是正确的。

曾经很好,现在很弱

1999 年 SQL Server 7 引入的散列算法适用于 1999 年。

  • 密码哈希加盐很好。
  • 最好将盐附加到密码中,而不是预先添加它。

但今天它已经过时了。它只运行一次哈希,它应该运行几千次,以阻止暴力攻击。

事实上,作为检查的一部分,Microsoft 的 Baseline Security Analyzer 会尝试暴力破解密码。如果它猜到了,它会将密码报告为弱密码。它确实得到了一些。

蛮力

为了帮助您测试一些密码:

DECLARE @hash varbinary(max)
SET @hash = 0x01009A664D796EDB2FA35E3B8FAB4DBA2FFB62F5426B67FE54A3
--Header: 0x0100
--Salt:   0x9A664D79
--Hash:   0x6EDB2FA35E3B8FAB4DBA2FFB62F5426B67FE54A3

DECLARE @password nvarchar(max)
SET @password = 'password'

SELECT
    @password AS CandidatePassword,
    @hash AS PasswordHash,
    
    --Header
    0x0100
    +
    --Salt
    CONVERT(VARBINARY(4), SUBSTRING(CONVERT(NVARCHAR(MAX), @hash), 2, 2))
    +
    --SHA1 of Password + Salt
    HASHBYTES('SHA1', @password + SUBSTRING(CONVERT(NVARCHAR(MAX), @hash), 2, 2))

SQL Server 2012 和 SHA-512

从 SQL Server 2012 开始,Microsoft 转而使用 SHA-2 512 位:

hashBytes = 0x0200 | fourByteSalt | SHA512(utf16EncodedPassword+fourByteSalt)

将版本前缀更改为0x0200

SELECT 
   name, CAST(password AS varbinary(max)) AS PasswordHash
FROM sys.syslogins

name  PasswordHash
----  --------------------------------
xkcd  0x02006A80BA229556EB280AA7818FAF63A0DA8D6B7B120C6760F0EB0CB5BB320A961B04BD0836 0C0E8CC4C326220501147D6A9ABD2A006B33DEC99FCF1A822393FC66226B7D38
  • 版本:0200 (SHA-2 512 位)
  • 盐:6A80BA22
  • 哈希(64 字节):9556EB280AA7818FAF63A0DA8D6B7B120C6760F0EB0CB5BB320A961B04BD0836 0C0E8CC4C326220501147D6A9ABD2A006B33DEC99FCF1A822393FC66226B7D38

这意味着我们使用 salt 后缀对 UTF-16 编码的密码进行哈希处理:

  • SHA512( "正确的马电池订书钉" + 6A80BA22)
  • SHA512( 63006f0072007200650063007400200068006f0072007300650020006200610074007400650072007900200073007400610070006c006500+ 6A80BA22)
  • 9556EB280AA7818FAF63A0DA8D6B7B120C6760F0EB0CB5BB320A961B04BD0836 0C0E8CC4C326220501147D6A9ABD2A006B33DEC99FCF1A822393FC66226B7D38
于 2013-08-09T19:20:23.983 回答
16

我相信 pwdencrypt 正在使用散列,因此您无法真正反转散列字符串 - 该算法的设计是不可能的。

如果您要验证用户输入的密码,通常的技术是将其散列,然后将其与数据库中的散列版本进行比较。

这是您可以验证用户输入的表的方式

SELECT password_field FROM mytable WHERE password_field=pwdencrypt(userEnteredValue)

将 userEnteredValue 替换为(大吃一惊)用户输入的值 :)

于 2008-10-06T06:33:03.290 回答
12

你意识到你可能正在为自己的未来做一根棍子。pwdencrypt() 和 pwdcompare() 是未记录的函数,在 SQL Server 的未来版本中可能表现不同。

为什么不在访问数据库之前使用可预测的算法(例如 SHA-2 或更好的算法)对密码进行哈希处理?

于 2008-10-06T08:11:12.187 回答
4

你真的不应该对密码进行解密。

您应该对输入应用程序的密码进行加密,并与数据库中的加密密码进行比较。

编辑 - 如果这是因为忘记了密码,则设置一个机制来创建新密码。

于 2008-10-06T09:24:40.387 回答
1

您无法再次解密此密码,但还有另一种名为“pwdcompare”的方法。这是一个如何将其与 SQL 语法一起使用的示例:

USE TEMPDB
GO
declare @hash varbinary (255)
CREATE TABLE tempdb..h (id_num int, hash varbinary (255))
SET @hash = pwdencrypt('123') -- encryption
INSERT INTO tempdb..h (id_num,hash) VALUES (1,@hash)
SET @hash = pwdencrypt('123')
INSERT INTO tempdb..h (id_num,hash) VALUES (2,@hash)
SELECT TOP 1 @hash = hash FROM tempdb..h WHERE id_num = 2
SELECT pwdcompare ('123', @hash) AS [Success of check] -- Comparison
SELECT * FROM tempdb..h
INSERT INTO tempdb..h (id_num,hash) 
VALUES (3,CONVERT(varbinary (255),
0x01002D60BA07FE612C8DE537DF3BFCFA49CD9968324481C1A8A8FE612C8DE537DF3BFCFA49CD9968324481C1A8A8))
SELECT TOP 1 @hash = hash FROM tempdb..h WHERE id_num = 3
SELECT pwdcompare ('123', @hash) AS [Success of check] -- Comparison
SELECT * FROM tempdb..h
DROP TABLE tempdb..h
GO
于 2008-10-06T06:35:35.593 回答
1

快速谷歌表明 pwdencrypt() 不是确定性的,您的语句 select pwdencrypt('AAAA') 在我的安装中返回不同的值!

另见这篇文章http://www.theregister.co.uk/2002/07/08/cracking_ms_sql_server_passwords/

于 2008-10-06T08:33:28.010 回答