浅谈网站用户密码安全
浅谈网站用户密码安全
转载自:http://blog.sina.com.cn/s/blog_624f972301013113.html
前段时间,不少重量级网站纷纷爆出密码泄露问题,搞得人心惶惶,很多密码万年不换的用户也被迫更换新密码,
那么,是为什么黑客可以如此轻易的获得用户密码呢?俗话说,道高一尺,魔高一丈,作为一个DBA,我们有什么手段可以提高用户信息的安全性呢?
信息泄露通常的原因之一是由于前端程序员忽略了对用户输入的检查,导致了SQL注入问题,造成数据库内容泄露。
程序员的水平参差不齐,也没有做充分的code review,所以这样的问题很难彻底避免,国内一些著名的购物网站,技术网站都存在这样的问题。
保证用户信息安全的第一道防线就是:密码加密。
现代加密算法主要分为三种:哈希算法(也叫杂凑算法,散列算法,单向加密算法),对称算法,非对称算法(也叫公开密钥算法),
详情请浏览《密码学》或类似书籍。
通常我们使用哈希算法加密密钥,哈希算法的特点是无法解密,所以即使数据库内容泄露,也很难反推出用户的密码。在
数据库中只存放用哈希算法加密后的密文,在比较时把用户输入的密码加密后与密文进行比较,就可以验证用户输入的密码是否正确。
SQL Server 2005起内置哈希函数,支持MD2 | MD4 | MD5 | SHA | SHA1算法。
由于MD和SHA的碰撞已经比较容易被找到(简单的说,就是能根据密文找到“密码”),所以建议使用SHA1算法。
除了哈希算法,非对称算法也是一个加密密钥的好主意,只需要丢弃私钥,也就变成了单向的加密,
但非对称算法的开销比哈希算法要大得多,所以在实际应用中使用得不多。
示例如下:
1 浅谈网站用户密码安全 2 3 4 存储密钥: 5 USE [tempdb] 6 GO 7 CREATE TABLE UserLogin 8 ( 9 UserName NVARCHAR(50) PRIMARY KEY, 10 Password VARBINARY(256) 11 ) 12 GO 13 INSERT INTO UserLogin 14 VALUES('Username', CONVERT(VARBINARY(256), PWDENCRYPT('pass@word'))) 15 INSERT INTO UserLogin 16 VALUES('Username1', CONVERT(VARBINARY(256), PWDENCRYPT('pass@word'))) 17 这时候我们 SELECT * FROM UserLogin 会发现,两次加密的密文不一样,这就是salt的功效了。 18 验证用户登录: 19 CREATE PROCEDURE up_UserLogin 20 ( 21 @UserName NVARCHAR(50), 22 @Password VARCHAR(100) 23 ) 24 AS 25 DECLARE @ret INT 26 SELECT @ret = PWDCOMPARE(@Password,Password) FROM UserLogin 27 WHERE UserName = @UserName 28 29 IF @ret = 1 30 --正确登录 31 RETURN 0 32 ELSE 33 --错误登录,可能用户不存在或密码错误 34 RETURN 1 35 GO 36 DECLARE @ret INT 37 EXEC @ret = up_UserLogin 'Username', 'pass@word' 38 SELECT @ret 39 由于PWDENCRYPT和PWDCOMPARE这两个函数是SQL Server未公开的函数,可能在某一个版本就废弃了,所以不建议使用在可能会升级的生产系统中。下面是另一个使用salt的示例: 40 CREATE TABLE UserLogin 41 ( 42 UserName NVARCHAR(50) PRIMARY KEY, 43 Password BINARY(20), 44 SignupTime DATETIME 45 ) 46 GO 47 INSERT INTO UserLogin 48 VALUES('Username', HASHBYTES('SHA1', CONVERT(VARCHAR(50), HASHBYTES('MD5', N'Username'), 2) + 'pass@word' + CONVERT(VARCHAR(50), HASHBYTES('MD5', CONVERT(NVARCHAR(50), GETDATE(), 121)), 2)), GETDATE()) 49 SELECT * FROM UserLogin 50 GO 51 CREATE PROCEDURE up_UserLogin 52 ( 53 @UserName NVARCHAR(50), 54 @Password VARCHAR(100) 55 ) 56 AS 57 DECLARE @ret INT, @CiperText BINARY(20), @SignupTime DATETIME 58 SELECT @CiperText = Password, @UserName = UserName, @SignupTime = SignupTime 59 FROM UserLogin 60 WHERE UserName = @UserName 61 62 IF @CiperText IS NOT NULL AND @CiperText = HASHBYTES('SHA1', CONVERT(VARCHAR(50), HASHBYTES('MD5', @UserName), 2) + @Password + CONVERT(VARCHAR(50), HASHBYTES('MD5', CONVERT(NVARCHAR(50), @SignupTime, 121)), 2)) 63 --正确登录 64 RETURN 0 65 ELSE 66 --错误登录,可能用户不存在或密码错误 67 RETURN 1 68 GO 69 DECLARE @ret INT 70 EXEC @ret = up_UserLogin 'username', 'pass@word' 71 SELECT @ret 72 73 本示例使用了用户名和注册时间的MD5值作为salt。 74 75 使用哈希算法加密后的密码是相对安全了,但是我们要保密的不只是用户密码而已,为了防备SQL注入攻击,最常见的方法就是强制参数校验。Developer那边我们DBA管不了,那么数据库这边总是我们说了算吧?建议禁止sa账号的使用,严格限制db_owner的使用,程序用的账号建议拒绝db_reader/db_writer权限,只允许执行存储过程的权限,所有的存储过程经过DBA审阅后加密创建,严格控制动态SQL语句的使用。这样就可以防备使用SQL注入工具攻击数据库的低级黑客了。当然,这样肯定是给开发带来一点不便的,不过为了安全,另外还可以提高数据库访问的效率,还是值得的。要注意的一点是,控制数据库的读写权限可以很方便的用db_reader/db_writer,但是没有一个角色具有全部存储过程的执行权限的。一般来说,要给账号赋予全部存储过程的执行权限,有两个方案,一个是DBA在创建账号后人工给账号授权,另一个是在创建存储过程时自动授权。下面这段触发器的代码会在创建存储过程时给角色db_procexecutor授予执行权限。 76 CREATE TRIGGER TR_DDL_AUTO_GRANT_NEW_PROC_TO_db_procexecutor 77 ON DATABASE 78 FOR CREATE_PROCEDURE 79 AS 80 DECLARE @PROC_NAME NVARCHAR(300), @SQL NVARCHAR(4000) 81 SET @PROC_NAME = QUOTENAME(EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname')) + '.' + QUOTENAME(EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname')); 82 SET @SQL = N'GRANT EXECUTE ON ' + @PROC_NAME + ' TO [db_procexecutor]'; 83 EXEC (@SQL) 84 GO 85 这点小伎俩是防不了真正的黑客的,不过我们可以在里面加一些Honeycombs来提高一点安全性,里面放上一些名字看上去很诱人的存储过程,比如GetUserInfo之类的,一执行马上给DBA发个短信。 86 信息泄露的另一个原因是员工被收买,事实上,这是一个简单得多的途径,防备这个的方法通常是加密。SQL Server支持对称加密和非对称加密,欲知详情,请翻阅联机丛书。不过这两种加密是要修改应用程序的,好处是可以自由控制要加密的内容,而不用全部加密,缺点是加密后的数据无法使用索引做范围查找或模糊查找。SQL Server还支持TDE(透明加密),不需要修改程序即可实现加密,索引什么的都没有问题,缺点是只能全库加密。当然,加密也只能防备直接拷贝数据库,多收买几个人就可以搞定了:D 87 下面是使用对称加密算法加密表中一个字段的示例: 88 CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'Pass@word3'; 89 GO 90 CREATE CERTIFICATE DatabaseCert 91 AUTHORIZATION dbo 92 WITH SUBJECT = 'Database Certificate', 93 EXPIRY_DATE = '12/31/2012';--mm/dd/yyyy 94 GO 95 CREATE SYMMETRIC KEY SymmetricKey WITH ALGORITHM = TRIPLE_DES 96 ENCRYPTION BY CERTIFICATE DatabaseCert; 97 GO 98 --以上代码为创建证书和对称密钥,为指定用户授予权限的代码略。除SQL授权外,也可用密码等手段保护证书和密钥,或者把密钥直接存储在数据库之外。 99 CREATE TABLE test_encrypted 100 ( 101 id int primary key, 102 name_encrypted varbinary(128) 103 ) 104 GO 105 --创建VIEW,以简化对加密数据的查询,直接查询视图即可得到解密后的数据 106 CREATE VIEW dbo.test 107 WITH SCHEMABINDING 108 AS 109 SELECT id, CONVERT(varchar(10), DecryptByKeyAutoCert(cert_ID('DatabaseCertNoEKM'), NULL, name_encrypted)) AS name 110 FROM dbo.test_encrypted 111 GO 112 --创建INSTEAD OF触发器,以简化对加密数据的增删改,直接对视图操作,数据库中存储的即为加密后的数据。 113 CREATE TRIGGER [dbo].[test_insert] 114 ON [dbo].[test] 115 INSTEAD OF INSERT 116 AS 117 BEGIN 118 SET NOCOUNT ON; 119 OPEN SYMMETRIC KEY SymmetricKey 120 DECRYPTION BY CERTIFICATE DatabaseCert; 121 122 INSERT INTO dbo.test_encrypted (name_encrypted) 123 SELECT EncryptByKey(Key_GUID('SymmetricKey'), name) 124 FROM inserted 125 END 126 GO 127 CREATE TRIGGER [dbo].[test_update] 128 ON [dbo].[test] 129 INSTEAD OF UPDATE 130 AS 131 BEGIN 132 SET NOCOUNT ON; 133 OPEN SYMMETRIC KEY SymmetricKey 134 DECRYPTION BY CERTIFICATE DatabaseCert; 135 136 UPDATE dbo.test_encrypted SET name_encrypted = EncryptByKey(Key_GUID('SymmetricKey'), inserted.name) 137 FROM inserted INNER JOIN deleted ON inserted.id = deleted.id 138 WHERE dbo.test_encrypted.id = deleted.id; 139 END 140 GO 141 CREATE TRIGGER [dbo].[test_delete] 142 ON [dbo].[test] 143 INSTEAD OF DELETE 144 AS 145 BEGIN 146 SET NOCOUNT ON; 147 DELETE dbo.test_encrypted 148 FROM deleted WHERE dbo.test_encrypted.id = deleted.id; 149 END 150 GO 151 这种方法能够简化对加密的使用,使得应用程序基本无须修改,即可享受加密带来的好处。不过INSTEAD OF触发器有个缺点,基表存在IDENTITY,TIMESTAMP,计算列等无需赋值字段时,INSERT可以无需为该字段赋值,但使用INSTEAD OF触发器后,必须为这些列提供值。有人想出了一个巧妙的办法,绕开了这个限制,不过这是别人的商业机密,未得授权不便透露。 152 以下是TDE的示例,TDE的使用要简单得多: 153 USE master; 154 GO 155 CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'Pass@word2'; 156 GO 157 CREATE CERTIFICATE ServerCert 158 AUTHORIZATION EncryptAdmin 159 WITH SUBJECT = 'Server Certificate', 160 EXPIRY_DATE = '12/31/2012';--mm/dd/yyyy 161 GO 162 USE Test_DB; 163 GO 164 CREATE DATABASE ENCRYPTION KEY 165 WITH ALGORITHM = AES_128 166 ENCRYPTION BY SERVER CERTIFICATE ServerCert; 167 GO 168 ALTER DATABASE Test_DB 169 SET ENCRYPTION ON; 170 GO 171 注意使用TDE后,要注意备份并妥善保管服务器证书,否则数据库服务器崩溃后,数据库中的数据将无法恢复。当然,也要注意证书的安全,否则加密形同虚设。 172 173 注:本文所介绍的示例代码可在SQL Server中运行,其中加密算法需要SQL Server 2005或以上版本,TDE需要SQL Server 2008 Datacenter/Enterprise 或以上版本。示例代码不包含错误处理,授权等内容,用于生产系统前请自行补充该部分内容。任何建议或指正,敬请联系lvke1978#hotmail 174 175 参考资料: 176 [1] SQL Server Books Online 177 [2] Bruce Schneier, “Applied Cryptography Second Edition: protocols, algorithms, and source code in C”, John Wiley & Sons, Inc. 1996 178 [3] Kevin Kenan, “数据库加密——最后的防线”, 李彦智,马超,林滨 译,电子工业出版社,2006