SQL Server 對稱密鑰在數據加密中的應用

3月19日,網上爆出微博數據泄露。

微博方亦迅速回應,表示數據泄露屬實。

微博表示一直有提供根據通訊錄手機號查詢微博好友暱稱的服務,用戶授權後可以使用該服務。

但微博不提供用戶性別和身份證號等信息,也沒有“根據用戶暱稱查手機號”的服務。

因此這起數據泄露不涉及身份證、密碼,對微博服務沒有影響。

目前微博已經及時強化安全策略,並將不斷強化。

相對來說此次數據泄露引起的危險性不大,不過又引起了我們對數據安全和隱私的關注,且近幾年重大的數據泄露事件不斷,數據泄露帶來的影響也是不可估量的,這足以引起我們對數據安全的重視。作爲一個數據管理人員,防範於未然,是必要的事情。

防止數據泄露的方案之一是防止未授權的用戶訪問數據,我們可以使用視圖隱藏原表中的私密數據,開放公開數據;當然我們也可以給以指定列的權限,限制用戶訪問私密數據。限定指定列的訪問權限,在前面的SQL Server數據庫用戶權限等文章中都有涉獵,需要的可以查閱前文。第二種方案即是對數據進行加密,而加密可以前端直接對信息進行加密後存儲到數據庫,另外一種方式是在數據庫進行加密。

作爲數據管理人員,在數據庫中進行的數據加密解密是我們所關注的重點。所以從本文開始的幾篇文章中,我將把SQL Server數據庫中的數據的加密方案逐一介紹給大家,開啓我們數據的安全之路。

 

我們先從使用對稱祕鑰加密數據開始。首先搭建我們的測試環境,創建數據庫EncryptionTest ,在數據庫中創建表EncryptionData, 加密證書的數據庫主祕鑰,使用DMK加密的證書,以及使用證書加密的對稱祕鑰,該對稱祕鑰將用來加密數據:

--創建測試數據庫
CREATE DATABASE EncryptionTest ON PRIMARY
(NAME='EncryptionTest',FILENAME='D:\database\EncryptionTest.mdf')
LOG ON
(NAME='EncryptionTest_log',FILENAME='D:\database\EncryptionTest_log.ldf')
USE EncryptionTest
GO
--創建測試表
CREATE TABLE EncryptionData(id      INT,EncryptionCol VARBINARY(128))
--創建數據庫主密鑰
CREATE MASTER KEY ENCRYPTION BY PASSWORD='[J,XJK8|AwE*rLk'
--創建證書
CREATE CERTIFICATE CertData
WITH SUBJECT='DMK Certificate CertData'
--創建對稱密鑰
CREATE SYMMETRIC KEY SYKey_Data
WITH ALGORITHM=AES_256
ENCRYPTION BY CERTIFICATE CertData

使用SQL Server提供的加密函數 ENCRYPTBYKEY 實現數據加密,並查看加密後的效果:

INSERT INTO EncryptionData VALUES(1,ENCRYPTBYKEY(KEY_GUID('SYKey_Data'),'Jack'))
--打開對稱密鑰
OPEN SYMMETRIC KEY SYKey_Data
DECRYPTION BY CERTIFICATE CertData
INSERT INTO EncryptionData VALUES(2,ENCRYPTBYKEY(KEY_GUID('SYKey_Data'),'Jack'))
--關閉對稱密鑰
CLOSE SYMMETRIC KEY SyKey_Data
SELECT * FROM EncryptionData

注意:

  • 沒有打開對稱祕鑰時,加密函數ENCRYPTBYKEY不能將字符串轉換爲加密字符串,返回的NULL。

  • 對稱祕鑰打開是會話級別的

  • 對稱祕鑰打開後,如果沒有在會話中顯示關閉,直到會話結束,對稱祕鑰會一直保持開啓狀態

爲了屏蔽非相關人員查看數據加密內容,保護數據安全,我們可以將插入腳本封裝爲過程:

CREATE PROC InsertEncryptData
      @id INT,
      @EncryptionCol VARCHAR(20)
AS
BEGIN
      --打開對稱密鑰
      OPEN SYMMETRIC KEY SYKey_Data
      DECRYPTION BY CERTIFICATE CertData
      INSERT INTO EncryptionData VALUES(@id,ENCRYPTBYKEY(KEY_GUID('SYKey_Data'),@EncryptionCol))
      --關閉對稱密鑰
      CLOSE SYMMETRIC KEY SyKey_Data
END
GO

執行驗證封裝過程,已經實現了加密的功能:

然後創建一個用戶,僅僅給以執行該存儲過程的權限,創建一個角色給以證書的控制權限,賦予角色查看對稱祕鑰定義的權限:

CREATE USER Jack WITHOUT LOGIN
GRANT EXEC ON OBJECT::InsertEncryptData TO Jack
CREATE ROLE OpenSysKey
GRANT CONTROL ON CERTIFICATE::CertData TO OpenSysKey
GRANT VIEW DEFINITION ON SYMMETRIC KEY::SyKey_Data TO OpenSysKey
EXEC sys.sp_addrolemember OpenSysKey,Jack

然後在Jack用戶上下文中執行存儲過程,並檢查結果:

EXEC AS USER='Jack'
EXEC InsertEncryptData 4,'Jack2'
REVERT
SELECT * FROM EncryptionData

這樣我們可以看到,通過調用過程InsertEncryptData 可以成功的實現加密的功能。

注意:如果沒有賦予證書控制權限及查看對稱祕鑰定義權限,會報如下錯誤:

消息15151,級別16,狀態1,過程InsertEncryptData,第7 行

無法對對稱密鑰'SYKey_Data' 執行查找,因爲它不存在,或者您沒有所需的權限。

(1 行受影響)

消息15315,級別16,狀態1,過程InsertEncryptData,第13 行

密鑰'SyKey_Data' 未打開。請先打開它,然後再使用它。

 

但是我們可以通過資源管理器右擊過程,生成腳本,或者對有查看InsertEncryptData 定義的用戶,仍然能夠完全掌握數據加密的過程,我們給Jack用戶賦予查看存儲過程InsertEncryptData 定義的權限,然後在Jack用戶上下文中使用sp_helptext 查看過程的定義,如下:

--爲Jack用戶賦予查看存儲過程InsertEncryptData定義的權限
GRANT VIEW DEFINITION ON OBJECT::InsertEncryptData TO Jack
EXEC AS USER='Jack'
EXEC sp_helptext 'InsertEncryptData'
REVERT

從結果中可以完整的看到數據加密的過程,這說明單單是封裝存儲過程還是不夠的,我們可以使用存儲過程的創建選項WITH ENCRYPTION 對存儲過程加密,下面我們直接修改上面創建的存儲過程,腳本如下:

ALTER PROC InsertEncryptData
      @id INT,
      @EncryptionCol VARCHAR(20)
WITH ENCRYPTION AS
BEGIN
      --打開對稱密鑰
      OPEN SYMMETRIC KEY SYKey_Data
      DECRYPTION BY CERTIFICATE CertData
      INSERT INTO EncryptionData VALUES
       (@id,ENCRYPTBYKEY(KEY_GUID('SYKey_Data'),@EncryptionCol))
      --關閉對稱密鑰
      CLOSE SYMMETRIC KEY SyKey_Data
END
GO

從對象資源管理器中查看過程,會發現此時過程左邊圖標比原先多了一個鎖的圖案:

此時我們再次查看過程的定義,會發現,雖然已經給了Jack用戶查看過程定義的權限,但是隻返回了文本已加密的提示:

至此,我們雖然儘可能的保證加密過程不被泄漏,但是存在一個比較明顯的漏洞,使得很容易試驗出加密的過程,即如果我們有查看系統視圖sys.key_encryptions 確定對稱祕鑰的加密方式,並且有查看系統視圖sys.symmetric_keys、sys.certificates  中的對稱祕鑰名稱和證書名稱,我們就很容易獲得數據的加密過程,從而對數據進行解密,因爲解密也是相當簡單的,只需知道對稱祕鑰名稱和加密證書名稱及加密方式即可,如下是解密腳本:

OPEN SYMMETRIC KEY SYKey_Data
DECRYPTION BY CERTIFICATE CertData
SELECT
      id,
      CONVERT(VARCHAR(MAX), DECRYPTBYKEY(EncryptionCol))
      ,DECRYPTBYKEY(EncryptionCol)
      ,EncryptionCol
FROM EncryptionData
--關閉對稱密鑰
CLOSE SYMMETRIC KEY SyKey_Data

爲了彌補這一漏洞,我們需要爲證書增加私鑰:

ALTER CERTIFICATE CertData
WITH PRIVATE KEY(ENCRYPTION BY PASSWORD='!<Ilc/cby=`Qpae')

再次在Jack用戶上下問中執行存儲過程,我們會發現報如下錯誤:

消息15334,級別16,狀態1,過程InsertEncryptData,第7 行

證書具有受用戶定義密碼保護的私鑰。若要啓用該私鑰,需要提供該密碼。

(1 行受影響)

消息15315,級別16,狀態1,過程InsertEncryptData,第13 行

密鑰'SyKey_Data' 未打開。請先打開它,然後再使用它。

打開證書,需要指定私鑰密碼,爲了能夠實現加密功能,我們將過程修改爲:

ALTER PROC InsertEncryptData
      @id INT,
      @EncryptionCol VARCHAR(20)
WITH ENCRYPTION AS
BEGIN
      
      --打開對稱密鑰
      OPEN SYMMETRIC KEY SYKey_Data
      DECRYPTION BY CERTIFICATE CertData
      WITH PASSWORD='!<Ilc/cby=`Qpae'
      INSERT INTO EncryptionData VALUES(@id,ENCRYPTBYKEY(KEY_GUID('SYKey_Data'),@EncryptionCol))
      --關閉對稱密鑰
      CLOSE SYMMETRIC KEY SyKey_Data
END
GO

此時再次執行過程,發現可以對數據成功進行加密:

從查詢結果我們可以看到,前面報錯時,仍然有id=5的記錄被插入,只是此時加密列爲NULL。

這樣只要我們的密碼不泄漏,我們的數據相對來說還比較安全的。

而有授權的用戶,可能需要查看解密後的數據,這裏我們再給出解密的過程:

CREATE PROC SelectDecryptionData
WITH ENCRYPTION AS
BEGIN
      --打開對稱密鑰
      OPEN SYMMETRIC KEY SYKey_Data
      DECRYPTION BY CERTIFICATE CertData
      WITH PASSWORD='!<Ilc/cby=`Qpae'
      SELECT
              id,
              EncryptionCol,
              CONVERT(VARCHAR(MAX), DECRYPTBYKEY(EncryptionCol)) AS DecryptionCol
      FROM EncryptionData
      --關閉對稱密鑰
      CLOSE SYMMETRIC KEY SyKey_Data
END

注意這裏封裝的解密過程也是加密的

下面我們賦予Jack 執行存儲過程SelectDecryptionData 的權限,並檢驗解密結果:

GRANT EXEC ON OBJECT::SelectDecryptionData TO Jack
EXEC AS USER='Jack'
EXEC SelectDecryptionData
REVERT

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章