防止开发人员获取到敏感数据(SQL Server的数据加密简介)
背景
有时候,我们还真的会碰到这样的需求:防止开发人员获取到敏感数据。也许你觉得很简单,把开发和运营分开不就可以了吗?是的,如果公司有专门的运营团队的话,但对于很多小公司来说,几个人的开发团队就兼顾了需求分析、设计、开发、测试、调试、部署和运营了,数据库密码知道,程序代码全有,怎么办?——必须对数据库里的数据进行加密,这是唯一的办法。
也许你还是不明白,什么东西需要瞒着我们了不起的程序员,好吧,我直说了:工资!假如你的公司让你做一个工资系统,你会不会有这方面的顾虑,一旦工资信息被公开,后果必定是很严重的,也许老板对你很信任,认为让你知道没什么问题,但其他开发人员呢?后来接手你的工作的人呢?所以必须考虑这个问题。而且,还外带一个需求:员工自己可以用自己的“薪资查看密码”来查看自己的工资(只能看自己的),每个人自己的“薪资查看密码”都不一样。另外不需要描述的隐藏需求还有:将来必定是要对薪资做统计做报表的。
相关代码
SQL Server(2005及之后的版本)提供了内置的加密机制,加密方式有两大类,一类是对称加密,另一类则是非对称加密。
SQL Server的对称加密示例代码:
--创建一个对称密钥,其实只需要创建一次,不用每次都创建,这个对称密钥密码为123456(嗯,大多数人认为的密码),密码是nvarchar类型的
CREATE SYMMETRIC KEY my_symetric_key WITH ALGORITHM = DESX ENCRYPTION BY PASSWORD = N''; --使用一个对称密钥前必须打开它,而且要提供创建它时所使用的密码,密码不对的话就会打开失败
OPEN SYMMETRIC KEY my_symetric_key DECRYPTION BY PASSWORD = N''; --只能加密字符串,如果要加密数字,就用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''; 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''); --显示出解密后的明文
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的内容则使用员工的各自的公钥进行加密。
现在我们来实践一下:
--创建一个员工表
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''; --初始化一些数据
insert into hr_emp (emp_no, name_c) values ('', '张三');
insert into hr_emp (emp_no, name_c) values ('', '李四');
insert into hr_emp (emp_no, name_c) values ('', '王五');
insert into hr_emp (emp_no, name_c) values ('', '赵六'); insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),8000.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),4000.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),3000.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Cash', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),4500.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),1234.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),800.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),765.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Allowance', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),980.00)), 0); insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),0.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),-440.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', 'Deduct', EncryptByAsymKey(AsymKey_ID('salary_mgr_key'), Convert(nvarchar(100),0.00)), 0);
insert into hr_salary (emp_no, type, amount, amount_view) values('', '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'')))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'')))) 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'')) FROM sys.asymmetric_keys WHERE name='salary_mgr_key';
若结果为1则正确,若结果为0或出现异常则不正确。
修改密码是很麻烦的事情,相当于重新创建一对非对称密钥,你得把整个加密好的数据用旧的密钥解密好,再用新的密钥加密。
最后还必须强调一点:整个服务器程序都不要保存密码,否则前功尽弃,密码必须由用户在使用的时候提供,并且只暂存于Session中,Session丢失的话必须要求用户重新输入密码。
防止开发人员获取到敏感数据(SQL Server的数据加密简介)的更多相关文章
- SQL Server的数据加密简介
防止开发人员获取到敏感数据(SQL Server的数据加密简介) 背景 有时候,我们还真的会碰到这样的需求:防止开发人员获取到敏感数据.也许你觉得很简单,把开发和运营分开不就可以了吗?是的,如果公司有 ...
- 第八篇 SQL Server安全数据加密
本篇文章是SQL Server安全系列的第八篇,详细内容请参考原文. Relational databases are used in an amazing variety of applicatio ...
- 【译】第八篇 SQL Server安全数据加密
本篇文章是SQL Server安全系列的第八篇,详细内容请参考原文. Relational databases are used in an amazing variety of applicatio ...
- [转帖]sql server版本特性简介、版本介绍简介
sql server版本特性简介.版本介绍简介 https://www.cnblogs.com/gered/p/10986240.html 目录 1.1.sql server的版本信息 1.2.版本重 ...
- SQL Server 系统表简介
SQL Server 系统表简介 系统目录是由描述SQL Server 系统的数据库.基表.视图和索引等对象的结构的系统表组成.SQL Server 经常访问系统目录,检索系统正常运行所需的必要信息. ...
- .NET Core开发日志——Linux版本的SQL Server
SQL Server 2017版本已经可以在Linux系统上安装,但我在尝试.NET Core跨平台开发的时候使用的是Mac系统,所以这里记录了在Mac上安装SQL Server的过程. 最新的SQL ...
- .NET Core开发日志——ADO.NET与SQL Server
在.NET世界,如果想要对数据库进行操作,总少不了ADO.NET的身影.在.NET Core里同样离不开那些熟悉的类库与API.这里简略地介绍下如何通过ADO.NET对SQL Server进行不同的处 ...
- sql server版本特性简介、版本介绍简介
1.SQL Server 版本简介 1.1.sql server的版本信息 年 代 版 本 大版本号 1993年 SQL Server for Windows NT 4.21 1994年 ...
- 【转】SQL SERVER 开窗函数简介
在SQL SERVER 2005/2008支持两种排名开窗函数和聚集开窗函数. 以SQL SERVER中分面页为例,按时间顺序列出定单号. WITH OrderInfo AS ( SELECT ROW ...
随机推荐
- dictionaryWithContentsOfFile:方法
dictionaryWithContentsOfFile:方法的功能是创建一个字典,将字典中的内容设置为指定文件中的所有内容, 语法:(id)dictionaryWithContentsOffilE. ...
- sqlite3 根据实体自动生成建表语句
public class BuildSqlTool { public static string GetCreateTableSql(object t) { //CREATE TABLE &quo ...
- linux下不同服务器间数据传输(rcp,scp,rsync,ftp,sftp,lftp,wget,curl)(zz)
linux下不同服务器间数据传输(rcp,scp,rsync,ftp,sftp,lftp,wget,curl) 分类: linux2011-10-10 13:21 8773人阅读 评论(1) 收藏 举 ...
- Spark如何使用Akka实现进程、节点通信的简明介绍
<深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...
- .net走向设计2—设计工具
1.思维导图 2.项目管理工具 3.常用UML工具 4.数据库设计工具
- Linux下VFP NEON浮点编译
http://blog.csdn.net/liujia2100/article/details/27236477 NEON:SIMD(Single Instruction Multiple Data ...
- 在rails中 Rendering Partials through Ajax
之前做.net的时候,自己做了一个showcontent的插件,用来加载页面的局部partial 之前采用的是ashx的方式 但rails里面不太方面,今天找到一个比较好的方法,试验成功 起初网上找到 ...
- Number()数据类型转换
Number() 如果是 Boolean 值, 和.如果是数字值,只是简单的传入和返回.如果是 .如果是 undefined,返回NaN. 如果是字符串,遵循下列规则: 如果字符串中只包含数字( ...
- hasOwnProperty和in
返回一个布尔值,指出一个对象是否具有指定名称的属性. hasOwnProperty 此方法无法检查该对象的原型链中是否具有该属in 可以检查原型链中是否具有该属
- Javascript重要知识点梳理
Javascript重要知识点梳理 一.Javascript流程控制 js中常用的数据类型 var关键字的使用 if – else if – else switch while for 二.Javas ...