SQL Server Always Encrypted

过去SQL Server有多种加密数据的方式,如透明数据加密(TDE)。这种技术是在数据库文件或者备份被盗用时,保护静态数据。然而对于可以访问数据库本身,或者任何拥有数据库的用户,可以获取秘钥、证书和密码(系统管理员、黑客诸如此类),是没有效果的。

SQL Server 2016新引入了Always Encrypted 功能,其设计的目的即时保护敏感数据,如手机号、身份证、银行卡号等等,可以同时加密静态和动态数据(内存中的数据也会被加密)。因此,这可以保护数据免受流氓管理员、备份窃贼和中间人攻击。和TDE不同,即Always Encrypted 允许你仅仅加密某些列,而不是整个数据库。

客户端库确保纯文本只在应用程序或中间层中显示,而不出现在应用程序和数据库之间。下面的说明中,我试图表明,无论是在数据库中,还是在应用程序和数据库之间的两个方向上(数据写入数据库,和数据库数据输出),数据都是密文形式:

这就带来了Always Encrypted的第一个限制:目前所有的客户端库都不支持它。事实上,目前,Always Encrypted 线程的提供者是ADO.NET 4.6,因此,你需要确保.NET Framework 4.6 被安装到任何需要和Always Encrypted 数据交互的客户端应用服务器上。

本文将介绍Always Encrypted的基础配置,给出一些示例,解释其所受限制。

SQL Server 2016 Always Encrypted概念

使用Always Encrypted 有几个核心概念:

  • 列主秘钥:这是用于保护列加密秘钥的秘钥。在加密任何列之前,你必须拥有至少一个列主秘钥。

  • 列加密秘钥:这个加密秘钥是实际上保护加密列的。

  • 列级别加密设置:必须将列设置为加密,并使用特定的列加密密钥、算法(目前只支持一种算法)和要使用的加密类型。

    • Deterministic:相同的字符串加密后为相同的密文,这可以用于特定的操作(如点查询,distinct查询,分组),可以创建索引

    • Randomized:更安全,但不能用于计量或任何操作(写/展示),并且不能创建索引

  • 连接字符串:为了使得客户端驱动能够理解正在使用列加密,连接字符串必须有下面的属性:


Column Encryption Setting = enabled;

应用代码本身,除连接字符串设置外,没有任何改变,因为不需要知道哪个列真正的加密过。

Always Encrypted 应用场景:

  • 数据库在组织内部,管理人员为组织外部人员

  • 数据库搭建在云上,如Azure数据库等

  • 数据库搭建在云上,管理人员亦是组织外部人员

Always Encrypted 示例

为了简化过程,我将在单个、本地服务器上展示示例。首先让我们创建一个数据库:

CREATE DATABASE AlwaysEncrypted
ON PRIMARY
(NAME='AlwaysEncrypted',FILENAME='D:\database\AlwaysEncrypted.mdf')
LOG ON
(NAME='AlwaysEncrypted_log',FILENAME='D:\database\AlwaysEncrypted_log.ldf')

创建列主秘钥

现在我将创建一个列主秘钥和列加密秘钥。在对象资源管理器中,展开数据库→安全→始终加密的秘钥。在那里你将看到两个节点,你可以右击第一个“列主秘钥”,创建列主秘钥。

对话框中并没有给出太多的选项,给予一个名称,选择秘钥存储源。我选择当前用户。注意,你可以创建多个列主秘钥(用于支持秘钥轮换)。

列主秘钥存储的位置有四种:

  1. 当前用户

  2. 本地计算机

  3. Azure 秘钥保管库

  4. 秘钥存储提供程序(CNG)

注意:在上面创建过程中,要生成证书,该证书可供拥有证书的账户通过客户端查看Always Encrypted 解密后的数据(后期文章将会阐述)。

创建列加密秘钥

接着,创建列加密秘钥:

类似的,这个对话框仅仅让你为列加密秘钥提供一个名称,并选择列主秘钥:

在我的机器,可以生成如下创建脚本(但是请不要试图拷贝这些脚本在你自己的机器上运行):


USE [AlwaysEncrypted]
/****** Object:  ColumnMasterKey [ColumnMasterKey]    Script Date: 2020/4/10 17:09:09 ******/
CREATE COLUMN MASTER KEY [ColumnMasterKey]
WITH
(
       KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
       KEY_PATH = N'LocalMachine/My/52B3AFBC309633E9256AFCA35D0D33203AC9BD51'
)
GO
USE [AlwaysEncrypted]
CREATE COLUMN ENCRYPTION KEY [ColumnEncryptedKey]
WITH VALUES
(
       COLUMN_MASTER_KEY = [ColumnMasterKey],
       ALGORITHM = 'RSA_OAEP',
       ENCRYPTED_VALUE = 0x01700000016C006F00630061006C006D0061006300680069006E0065002F006D0079002F00350032006200330061006600620063003300300039003600330033006500390032003500360061006600630061003300350064003000640033003300320030003300610063003900620064003500310065F54E706262859512B9990D2FDA8CD0DA8B71120875AA1B096CECBCD14822B6E0931789DE37C670063E83F2067B57E6C7AFC8A9D5442DD3EA0EF4E2D22B606645682A3B638F2E1A8C4984188783FFCC5A8047CD87BF9709FFC5F191DED58DA5288F347D2EE70171BC4356111F76023C06E0D47291D7C9BADECAED976CBCA628029F80DAE0706D8FD3530F40BFE39D05B93AF01D930ED0665A2AD93DC65A8FDDAA9EC3E263FBCE20359F4A343E715CC43C81559DC60220C040B28BFC94A1CBB5EB5E000CAB43A65787F580DA73E34F03074BD8B8B30491742CCB7BE323A169D8120BB4045D3CD1407B4AD9F5B3ECBEB051D2270813E59A04C11CD524E9D79AFA07E350A1E0E67B6378EB7DECD66CF587D9BC7096E6EC61437D96F8B991D32F7FC07D85BADA7E79769A3BF1FBAF6525AD049EF99FD18EA3D9F9DA3BFA48E85F70596D203558517F17F0450C0D6C705980DF2D03430854C498FA9A2AF67FE866932EE8DE429C788CF2B3DDEC91AF9116A94D958F754F12562EAEDA2E6E1F8E1330C61CF8DC1BB99C07DA26D314D2A5BEFC9FF949FA1E02443CCA0FB78D2753CC7146DA37A04B838D2A6C8ED89960D561AC20E7D0055CBCDA6F157D0EF040F7608C2BDC92840B1EECFEDC95FE6B094E2E16DFB3702D3560697F77F5953ADB3BA07898DE5B81A48CBAB3E072387FE56299E472679D9A847846B8DAF2B38C18B05F39
)
GO

现在秘钥已经创建好,我们可以创建一个表来使用它们。假设我们有员工表,我们想对员工姓名和工资加密。

指定加密列的语法有点晦涩。如我前面提到的,仅仅只支持一个加密算法,引用有点拗口:AEAD_AES_256_CBC_HMAC_SHA_256。同时,任何字符型数据列决定使用DETERMINISTIC类型加密时,必须使用BIN2字符集。

CREATE TABLE Employee(
       ID INT IDENTITY(1,1) PRIMARY KEY
       ,Name NVARCHAR(10) COLLATE Chinese_PRC_BIN2
              ENCRYPTED WITH(
                     ENCRYPTION_TYPE=DETERMINISTIC
                     ,ALGORITHM='AEAD_AES_256_CBC_HMAC_SHA_256'
                     ,COLUMN_ENCRYPTION_KEY=ColumnEncryptedKey
              )NOT NULL
       ,Salary INT
              ENCRYPTED WITH(
                     ENCRYPTION_TYPE=RANDOMIZED
                     ,ALGORITHM='AEAD_AES_256_CBC_HMAC_SHA_256'
                     ,COLUMN_ENCRYPTION_KEY=ColumnEncryptedKey
              ) NOT NULL
);
GO

在客户端SSMS中执行新增员工信息语句:

INSERT dbo.Employee(Name,Salary) SELECT N'Jack',20000;

消息 206,级别 16,状态 2,第 70 行

操作数类型冲突: nvarchar 与 nvarchar(4000) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'ColumnEncryptedKey', column_encryption_key_database_name = 'AlwaysEncrypted') 不兼容

我修正临时ad hoc SQL中的数据类型,我得到了另外一个错误:

DECLARE @Name NVARCHAR(15)='Jack',@Salary int=20000
INSERT dbo.Employee(Name,Salary) SELECT @Name,@Salary;

消息 33299,级别 16,状态 6,第 73 行

加密方案不匹配列/变量 '@Name'。列/变量的加密方案为 (encryption_type = 'PLAINTEXT'),行“2”附近的表达式预期其为 (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'ColumnEncryptedKey', column_encryption_key_database_name = 'AlwaysEncrypted') (或更弱)。

调用上面创建的增加会员的存储过程,我们得到上面相同的错误:

DECLARE @Name NVARCHAR(15)='Jack',@Salary int=20000
EXEC dbo.AddEmployee @Name,@Salary

创建新增员工信息、员工信息查询过程,以供程序调用:

/*
       新增员工信息
*/
CREATE  PROC dbo.AddEmployee
       @Name NVARCHAR(10)
       ,@Salary INT
AS
BEGIN
       INSERT INTO dbo.Employee(Name,Salary)
       SELECT @Name,@Salary
END
GO
/*
       根据员工姓名获得员工信息
*/
CREATE PROC dbo.GetEmployeeByName
       @Name NVARCHAR(10)
AS
BEGIN
       SELECT
              ID,Name,Salary
       FROM DBO.Employee
       WHERE Name=@Name COLLATE Chinese_PRC_BIN2
END
GO

VS开发测试

在Visual Studio的上,我将创建一个非常简单的Windows窗体应用程序,它将允许我填充和查询这个表。首先需要包括列加密设置属性的连接字符串,因此我的App.Config 有如下内容:

<connectionStrings>
   <add name="AEDB" connectionString="Data Source=.;Initial Catalog=AlwaysEncrypted;
   Integrated Security=True;Column Encryption Setting=Enabled"
   providerName="System.Data.SqlClient" />
</connectionStrings>

在我的窗体上添加两个文本框和两个按钮,允许我输入姓名和薪资,并向数据库表中插入一行数据;或者输入姓名,检索展示其工资。

新增员工信息按钮的脚本:

private void button1_Click(object sender, EventArgs e)
        {
            using (SqlConnection con = new SqlConnection())
            {
                con.ConnectionString = ConfigurationManager.ConnectionStrings["AEDB"].ToString();
                con.Open();
                using (SqlCommand cmd = new SqlCommand("dbo.AddEmployee", con))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    SqlParameter Nm = new SqlParameter("@Name", SqlDbType.NVarChar, 10);
                    Nm.Value = textBox1.Text;
                    SqlParameter Sal = new SqlParameter("@Salary", SqlDbType.Int);
                    Sal.Value = Convert.ToInt32(textBox2.Text);
                    cmd.Parameters.Add(Nm);
                    cmd.Parameters.Add(Sal);
                    cmd.ExecuteNonQuery();
                    MessageBox.Show("新增员工成功。");
                    textBox1.Clear();textBox2.Clear();
                }
            }
        }

查询员工薪资的脚本:

private void button2_Click(object sender, EventArgs e)
        {
            using(SqlConnection con=new SqlConnection())
            {
                con.ConnectionString = ConfigurationManager.ConnectionStrings["AEDB"].ToString();
                con.Open();
                using(SqlCommand cmd =new SqlCommand("dbo.GetEmployeeByName", con))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    SqlParameter Nm = new SqlParameter("@Name", SqlDbType.NVarChar, 10);
                    Nm.Value = textBox1.Text;
                    cmd.Parameters.Add(Nm);
                    SqlDataReader rdr = cmd.ExecuteReader();
                    while (rdr.Read())
                    {
                        textBox2.Text = rdr["Salary"].ToString();
                    }
                }
            }
        }

它非常粗糙和简陋,但它完成了工作:当您输入姓名和薪水并按下“新增员工信息”按钮时,它将其添加到数据库中,然后清除表单。如果只输入姓名按下另一个按钮,它将用该人员的薪水填充salary字段。

如果我们开启Profiler,我们可以看到下面的读写过程,参数在到达SQL Server之前已经加密了。


exec sp_describe_parameter_encryption N'EXEC [dbo].[AddEmployee] @Name=@Name, @Salary=@Salary',N'@Name nvarchar(10),@Salary int'

exec dbo.AddEmployee @Name=0x01BE14A713B21E6B357A0787010B45118E02D809C43CF75BA7E89EF00E08204709EA11B25936C51B97B6674B7846B9EFF0DF8EE3B66C97B798C3E2B6777D186ED4,@Salary=0x0118A0628238B637DCE8C041CCE05028743F5ADE119EED521AB75DF304CBEB5E43703A484FC3CA8E3DFC1D755A0160C32E9228C60671E9F912AF3981FDAAFBE52E

查询员工薪资

exec dbo.GetEmployeeByName @Name=0x01BE14A713B21E6B357A0787010B45118E02D809C43CF75BA7E89EF00E08204709EA11B25936C51B97B6674B7846B9EFF0DF8EE3B66C97B798C3E2B6777D186ED4

我们再回过头来查看数据库中的数据存储情况:

我们可以看到姓名列和薪资列都已经加密了。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,可用于构建高性能和可伸缩的网络应用程序。SQL Server 是由 Microsoft 开发的关系型数据库管理系统。SSL(Secure Sockets Layer)是一种加密协议,用于在客户端和服务器之间建立安全的通信连接。 Node.js 提供了许多用于与 SQL Server 进行交互的模块和库,其中一些支持使用 SSL 连接。通过使用 `mssql` 模块,我们可以在 Node.js 中连接到 SQL Server 数据库,并使用 SSL 加密来确保通信的安全性。 可以通过以下步骤在 Node.js 中使用 SSL 连接 SQL Server: 1. 安装 `mssql` 模块:使用 npm 包管理器安装 `mssql` 模块,可以使用以下命令:`npm install mssql` 2. 导入 `mssql` 模块:在 Node.js 文件中导入 `mssql` 模块,以便在代码中使用它:`const sql = require('mssql')` 3. 配置数据库连接参数:在代码中创建一个配置对象,包含 SQL Server 数据库的连接参数,包括主机名、端口号、数据库名称、用户名、密码以及 SSL 选项。例如: ``` const config = { server: 'hostname', port: 1433, database: 'database', user: 'username', password: 'password', options: { encrypt: true // 启用 SSL 加密 } }; ``` 4. 建立数据库连接:使用 `sql.connect()` 方法连接到 SQL Server 数据库,并传递上述配置对象作为参数。 ```javascript sql.connect(config).then(() => { // 连接成功后的操作 }).catch(err => { // 连接失败时的错误处理 }); ``` 通过以上步骤,我们可以在 Node.js 中使用 SSL 连接 SQL Server 数据库。使用 SSL 连接可以确保数据在传输过程中的安全性,防止数据被窃取或篡改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值