有時候,咱們還真的會碰到這樣的需求:防止開發人員獲取到敏感數據。也許你以爲很簡單,把開發和運營分開不就能夠了嗎?是的,若是公司有專門的運營團隊的話,但對於不少小公司來講,幾我的的開發團隊就兼顧了需求分析、設計、開發、測試、調試、部署和運營了,數據庫密碼知道,程序代碼全有,怎麼辦?——必須對數據庫裏的數據進行加密,這是惟一的辦法。程序員
也許你仍是不明白,什麼東西須要瞞着咱們了不得的程序員,好吧,我直說了:工資!假如你的公司讓你作一個工資系統,你會不會有這方面的顧慮,一旦工資信息被公開,後果一定是很嚴重的,也許老闆對你很信任,認爲讓你知道沒什麼問題,但其餘開發人員呢?後來接手你的工做的人呢?因此必須考慮這個問題。並且,還外帶一個需求:員工本身能夠用本身的「薪資查看密碼」來查看本身的工資(只能看本身的),每一個人本身的「薪資查看密碼」都不同。另外不須要描述的隱藏需求還有:未來一定是要對薪資作統計作報表的。算法
SQL Server(2005及以後的版本)提供了內置的加密機制,加密方式有兩大類,一類是對稱加密,另外一類則是非對稱加密。數據庫
SQL Server的對稱加密示例代碼:服務器
--建立一個對稱密鑰,其實只須要建立一次,不用每次都建立,這個對稱密鑰密碼爲123456(嗯,大多數人認爲的密碼),密碼是nvarchar類型的 CREATE SYMMETRIC KEY my_symetric_key WITH ALGORITHM = DESX ENCRYPTION BY PASSWORD = N'123456'; --使用一個對稱密鑰前必須打開它,並且要提供建立它時所使用的密碼,密碼不對的話就會打開失敗 OPEN SYMMETRIC KEY my_symetric_key DECRYPTION BY PASSWORD = N'123456'; --只能加密字符串,若是要加密數字,就用CONVERT函數先把數字轉爲字符串 DECLARE @strClearText NVARCHAR(100); SET @strClearText = N'3000.00'; --密文類型爲VARBINARY,用 DECLARE @strCipherText VARBINARY(MAX); SET @strCipherText = EncryptByKey(Key_GUID('my_symetric_key'), @strClearText); --顯示密文(密文其實爲二進制格式,你會看到其HEX文本) SELECT @strCipherText AS [密文]; --解密不須要提供密鑰名稱,SQL Server會根據當前上下文去尋找打開的對稱密鑰 DECLARE @strDecrypted VARBINARY(MAX); SET @strDecrypted = DecryptByKey(@strCipherText); --顯示出解密後的明文 SELECT Convert(NVARCHAR(100), @strDecrypted) AS [解密後的明文] --關閉這個密鑰 CLOSE SYMMETRIC KEY my_symetric_key; --之後還須要用這個密鑰的話就不用刪掉它 DROP SYMMETRIC KEY my_symetric_key;
SQL Server的非對稱加密示例代碼:函數
--建立一個非對稱密鑰(不用每次都建立),這個對稱密鑰密碼爲123456,使用RSA512算法,另外還有RSA1024和RSA2048,強度更高,可加密內容更長,密鑰生成速度也會慢很多,RSA512這裏足夠用了 CREATE ASYMMETRIC KEY my_asymetric_key WITH ALGORITHM = RSA_512 ENCRYPTION BY PASSWORD = N'123456'; DECLARE @strClearText NVARCHAR(100); SET @strClearText = N'3000.00'; --加密,和對稱加密不同,不須要提供密碼,也不須要打開密鑰 DECLARE @strCipherText VARBINARY(MAX); SET @strCipherText = EncryptByAsymKey(AsymKey_ID('my_asymetric_key'), @strClearText); --顯示密文 SELECT @strCipherText AS [密文]; --解密,必須提供生成密鑰時候的密碼,密碼不正確的話就會出錯 --密鑰選擇不正確的話會獲得NULL結果 DECLARE @strDecrypted VARBINARY(MAX); SET @strDecrypted = DecryptByAsymKey(AsymKey_ID('my_asymetric_key'), @strCipherText, N'123456'); --顯示出解密後的明文 SELECT Convert(NVARCHAR(100), @strDecrypted) AS [解密後的明文] --之後還須要用這個密鑰的話就不用刪掉它 DROP ASYMMETRIC KEY my_Asymetric_key;
另外可能用獲得的一些語句有:測試
--查看全部對稱密鑰 SELECT * FROM sys.symmetric_keys; --查看全部非對稱密鑰 SELECT * FROM sys.asymmetric_keys;
可能你還想說:其實這些加密程序也能作,爲何要用DBMS的功能來作?——方便。前面也提到了,工資這個東西未來必定要作統計,作報表的,若是用DBMS的功能來作,一個報表也許也就是一個連表查詢的SELECT語句,但用程序來作這種「連表查詢」的功能恐怕就很麻煩了。加密
在應付此次需求上面,我認爲比較適合用非對稱加密,即:誰均可以加密,但只有知道私鑰的人才能解密。例如我是工資管理員,我要給員工007設置工資爲3000,我就用007的公鑰對「3000」進行加密好了,這樣,007可以用本身的私鑰解密出本身的工資了,每一個員工都有不一樣的公私鑰,都只能查看本身的工資,那問題來了,對於我這個管理員來講,要查看全部員工的工資,豈不是要知道他們所有的私鑰才行?這樣豈不是很麻煩?是的,我此次是用了一點「數據冗餘」來解決這個麻煩,即:用兩列來保存工資信息,其中一列是真正的工資加密信息(amount),另外一列是給員工本身查看的工資信息(amount_view),amount_view是用amount生成的,amount的內容使用工資管理員的公鑰進行加密,而amount_view的內容則使用員工的各自的公鑰進行加密。spa
如今咱們來實踐一下:設計
--建立一個員工表 CREATE TABLE hr_emp( emp_no nvarchar(20) PRIMARY KEY, name_c nvarchar(20) NOT NULL, has_salary_pwd bit NOT NULL DEFAULT(0), ); --建立一個工資表 CREATE TABLE hr_salary( sal_id int PRIMARY key IDENTITY(1,1) NOT NULL, emp_no nvarchar(20) NOT NULL, type nvarchar(15) NOT NULL, amount varbinary(max) NOT NULL, amount_view varbinary(max) NOT NULL ); --增長一個外鍵約束 ALTER TABLE hr_salary ADD CONSTRAINT fk_salary_emp_ref_emp FOREIGN KEY (emp_no) REFERENCES hr_emp(emp_no); --建立一個非對稱密鑰 CREATE ASYMMETRIC KEY salary_mgr_key WITH ALGORITHM = RSA_512 ENCRYPTION BY PASSWORD = N'123456'; --初始化一些數據 insert into hr_emp (emp_no, name_c) values ('0008', '張三'); insert into hr_emp (emp_no, name_c) values ('0053', '李四'); insert into hr_emp (emp_no, name_c) values ('0055', '王五'); insert into hr_emp (emp_no, name_c) values ('0058', '趙六'); insert into hr_salary (emp_no, type, amount, amount_view) values('0008', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),8000.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0053', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),4000.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0055', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),3000.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0058', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),4500.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0008', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),1234.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0053', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),800.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0055', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),765.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0058', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),980.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0008', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),0.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0053', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),-440.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0055', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),0.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('0058', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),0.00)), 0);
如今來看hr_salary表中的內容的話,發現amount列是加密的,沒有密碼就無法知道其中的內容:調試
OK,咱們如今來解密:
select emp_no , type , Convert ( decimal( 16 ,2 ), Convert( nvarchar (100 ), DecryptByAsymKey (AsymKey_ID ( 'salary_mgr_key'), amount, N'123456')))as amount from hr_salary;
結果出來了,解密成功。
統計各個員工工資總數,並把中文名帶出來:
select e.emp_no, e.name_c, s.amount from hr_emp e left join (select emp_no, sum(Convert(decimal(16,2),Convert(nvarchar(100), DecryptByAsymKey(AsymKey_ID('salary_mgr_key'), amount, N'123456')))) as amount from hr_salary group by emp_no) s on e.emp_no=s.emp_no;
沒有壓力,對吧。
至於amount_view這列的處理,你們想一想也知道,方法其實前面都給出了,用CREATE ASYMMETRIC KEY語句來給每一個用戶建立密鑰,密鑰名稱可使用「ak+工號」這種規則,密碼能夠用隨機生成,將密碼告訴用戶,讓他們本身記住,這樣就OK了。只是處理的時候必須記得,amount發生變化的時候,amount_view也要跟着發生變化。
用戶提供的密碼是否正確能夠這樣驗證:
SELECT count(DECRYPTBYASYMKEY(AsymKey_ID('salary_mgr_key'), '', N'123456')) FROM sys.asymmetric_keys WHERE name='salary_mgr_key';
若結果爲1則正確,若結果爲0或出現異常則不正確。
修改密碼是很麻煩的事情,至關於從新建立一對非對稱密鑰,你得把整個加密好的數據用舊的密鑰解密好,再用新的密鑰加密。
最後還必須強調一點:整個服務器程序都不要保存密碼,不然前功盡棄,密碼必須由用戶在使用的時候提供,而且只暫存於Session中,Session丟失的話必需要求用戶從新輸入密碼。