在当今这个数据安全日益受到重视的时代,保护敏感数据的安全变得尤为重要。Microsoft SQL Server 2016引入了一项名为"Always Encrypted"的新特性,旨在增强数据库中敏感数据的安全性。这项特性允许客户端应用程序对敏感数据进行加密,而数据库引擎(无论是SQL Database还是SQL Server)都无法访问这些加密密钥。这样,"Always Encrypted"就实现了数据所有者(可以查看数据)和数据管理者(不应访问数据)之间的分离。
首先,需要创建一个新的数据库"AlwaysEncryption",并在其中创建一个名为"Customer"的表。假设"Email"和"SSN"列包含敏感信息。接下来,将创建一个新的ASP.NET Web应用程序"AlwaysEncryption"。在这个示例中,将使用Visual Studio 2015。从"New ASP.NET Project"模板列表中选择MVC,由于不关注身份验证,因此点击"Change Authentication"按钮选择"No Authentication"选项,然后点击"OK"。
接下来,将向"Models"文件夹添加一个新的实体数据模型文件。从"Entity Data Model"向导中选择"EF Designer from database",然后点击"Next"。在下一个向导屏幕中,选择"Microsoft SQL Server"作为数据源,然后点击"Continue"以建立连接。使用"Connection Properties"向导窗口连接到"AlwaysEncryption"数据库。进行快速的"Test Connection"以确保连接成功,然后点击"OK"继续。在下一个向导屏幕中,选择"Yes"单选按钮以在连接字符串中存储敏感的SQL凭据信息。然后提供上下文名称"AlwaysEncryptionContext",它将直接进入web.config文件中的连接字符串属性。
点击"Next"选择应用程序的Entity Framework版本。在本例中,选择了EF 6.x选项。点击"Next"选择"Customer"表,并命名模型命名空间,然后点击"Finish"以导入实体模型。Visual Studio 2015将在实际运行文本模板(T4)文件之前显示几个安全警告对话框。点击"OK"以生成上下文以及实体模型类。
现在,右键单击"Controller"文件夹并添加"New Scaffolding Item…"以使用Entity Framework创建MVC 5控制器以及视图。警告:在从数据库导入实体模型后,首先构建应用程序,然后再创建控制器或视图。选择"Customer"模型类和"AlwaysEncryptionContext"作为数据上下文类,并将新控制器命名为"CustomersController"。点击"Add"以创建控制器以及执行所有CRUD操作的视图。
接下来,设置路由配置文件中的默认控制器和操作,将其指向新"Customers"控制器以及"Index"操作方法。
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Customers", action = "Index", id = UrlParameter.Optional }
);
运行应用程序以在"Customer"表中填充一些记录。然后转到SQL ServerManagement Studio并运行以下查询以列出所有"Customer"记录。这将以明文形式显示所有数据。
要在Microsoft SQL 2016中加密敏感数据,请右键单击"Customer"表,然后从上下文菜单中选择"Encrypt Columns…"。这将显示"Always Encrypted"向导的"Introduction"屏幕,点击"Next"。现在得到了"Column Selection"向导屏幕,这是选择需要加密的选定表中所有数据列的地方。在这里,选择了"Email"和"SSN"列进行加密。注意"Encryption Type"列设置为"Deterministic",并选择了默认或自动生成的列加密密钥(CEK)。
Always Encrypted支持两种类型的加密:Deterministic Encryption和Randomized Encryption。Deterministic Encryption使用一种方法,对于任何给定的明文值,总是生成相同的加密值。使用Deterministic Encryption允许基于加密值对数据进行分组、过滤和连接表,但也可能允许未经授权的用户通过检查加密列中的模式来猜测加密值的信息。当可能的加密值集合较小时,这种弱点会增加,例如True/False,或North/South/East/West区域。Deterministic Encryption必须使用具有二进制2排序顺序的列校验规则对字符列进行排序。
Randomized Encryption使用一种方法,以不太可预测的方式加密数据。Randomized Encryption更安全,但阻止了基于加密列的等值搜索、分组、索引和连接。对于将用作搜索或分组参数的列,例如政府ID号,请使用Deterministic Encryption。对于数据,如机密调查评论,它们不与其他记录组合,并且不用于连接表,请使用Randomized Encryption。
在选择具有所需加密类型的列后,点击"Next",这将显示"Master Key Configuration"向导屏幕。为了使用CEK加密列,需要一个主加密密钥(MEK),它存储在数据库外部,可以是Windows证书存储或Azure密钥库。在这里,自动生成MEK并选择将其存储在"Current User"下的Windows证书存储中。点击"Next"查看"Run Settings"向导屏幕。这个屏幕警告用户在加密和解密过程中不要对表执行任何读写操作。这可能导致潜在的数据丢失。SQL Server2016建议在计划的维护窗口期间运行此设置。
点击"Next"查看"Summary"向导屏幕。在验证设置后点击"Finish"。这将显示"Results"向导屏幕,其中包含详细信息。在案例中,它通过了所有三个步骤:在Windows证书中生成了一个新的列主密钥CMK,生成了新的列加密密钥CEK,并在"Customer"表上执行了选定列的加密操作。现在再次运行选择脚本以查看选定列已加密。
完美,不是吗?!在表中加密列肯定会改变表架构,但并不像所有人预期的那样。列数据类型不会有任何变化。如果仔细查看表创建脚本,可以看到COLLATE以及ENCRYPTED WITH CEK和ALGORITHM。注意:到目前为止,SQL Server2016只提供了一种加密算法"AEAD_AES_256_CBC_HMAC_SHA_256"。
现在,重新运行MVC应用程序以查看加密数据如何在视图中显示。奇怪的是,它显示了以下错误消息。应用程序期望在"Email"列中输入字符串数据类型,但它被提供了加密数据流作为字节数组。为了解决这个问题,需要更新连接字符串以启用"column encryption settings"。这将允许应用程序在连接到数据库时自动加密/解密必要的列。
column encryption setting=enabled更新后的连接字符串如下所示。现在再次运行应用程序以查看列自动解密/加密。应用程序使用MEK和CEK来解密/加密列。如果仔细查看数据库安全性下的"Always Encrypted Keys"设置:右键单击CEK以生成其创建脚本:右键单击CMK以生成创建脚本。在这里,可以看到指向Windows证书的路径:如果去查找"Always Encrypted Certificate"在Current User…这里是双击时的证书详细信息。
假设备份数据库并在另一台机器/服务器上恢复。将MVC应用程序针对新数据库…认为,如果将数据库恢复到另一台机器或服务器上,应用程序会检索数据吗?答案是NO。为什么?因为,尽管可能从备份文件中恢复了数据库并有权访问所有CEK以加密/解密列,但没有存储在Windows证书存储中的主加密密钥。要获取MEK证书,需要联系所有者。这样做时,应用程序将无法使用提供的密钥解密/加密列,将收到一个错误。查看其他文章(尚未发布)以了解如何从Windows证书存储中导出和/或导入主加密密钥。