前言
更多运维相关知识:蓝队技能学习_其实防守也摸鱼的博客-CSDN博客

持续更新ing~
本文内容介绍
本文详细介绍了在SQL Server中实现数据安全防护的完整流程,包括:
- 使用ALTER TABLE语句为手机号字段添加部分掩码规则(保留前3后4位)
- 通过权限控制实现不同用户查看脱敏/明文数据
- 建立三层加密体系:数据库主密钥→证书→对称密钥(AES_256)
- 实现身份证字段的加密存储,将明文转换为二进制密文
- 通过触发器实现自动加密,确保新数据合规
- 创建测试用户并授予受限权限
- 验证普通用户只能看到脱敏数据
- 通过查询层水印(动态添加追踪标识)
- 存储层水印(DEFAULT约束自动标记数据来源)
文中还特别强调了政务系统中需使用国密算法(如SM4)替代国际算法的合规要求,并对比了存储加密与展示脱敏的技术差异。所有操作均配有详细SQL代码示例和错误排查方案,形成完整的数据安全防护闭环。
知识点
🛡️ 数据安全案例剖析
文档通过三个真实案例揭示了数据泄露的主要风险点:
- 核心问题:测试环境与生产环境未隔离、敏感信息未脱敏加密、缺乏溯源手段。
- 核心问题:技术防护措施缺失、安全缺陷修复不及时、系统日志留存不足6个月。
- 核心问题:人员安全意识薄弱、权限核验缺失、请求参数明文传输。
🛠️ 十大数据安全技术能力
为应对上述风险,文档提出了聚焦“五不”目标的一体化数据安全技术体系,包含十大核心能力:
| 监测类 | 态势感知 | 像安全警报系统,通过日志分析快速识别和预警安全风险。 |
| 接口审计 | 像监控摄像头,记录分析接口数据访问行为,发现异常和滥用。 | |
| 工具类 | 分类分级 | 识别数据敏感性(L1-L4),为不同级别数据配置差异化的存储、访问和审计策略。 |
| 数据加密 | 将数据转换成密文,确保即使数据被窃取也无法轻易解读。 | |
| 数据脱敏 | 隐藏或改变个人身份信息,在保留数据可用性的同时保护隐私。 | |
| 数据水印 | 在数据中嵌入隐形标记,一旦发生泄露可快速溯源到泄露源头。 | |
| 管控类 | 权限管控 | 严格控制数据访问权限,遵循最小化授权原则,防止未授权访问。 |
| 数据沙箱 | 提供独立安全的环境供开发运维使用,对数据操作行为进行全面审计和管控。 |
一、什么是“数据分类分级”?
这是数据安全治理的核心环节,目的是:
- 分类:按业务属性或数据类型(如个人信息、金融数据、企业数据)归类;
- 分级:根据数据泄露后可能造成的危害程度(对个人、组织、社会、国家安全的影响),划分为不同安全等级,通常分为4级:
- 核心数据:关系国家安全、经济运行命脉,泄露将造成特别严重危害;
- 重要数据:影响公共利益、行业稳定或大规模人群权益;
- 敏感一般数据:涉及个人隐私或企业商业秘密,泄露会造成中等以上损害;
- 常规一般数据:公开或低敏感度信息,泄露影响较小。
| 密码 | 身份鉴别信息 | 敏感一般数据(部分场景为重要数据) | 直接关联账户安全,泄露可导致身份冒用、资金损失。 |
| 手机号 | 个人通信标识 | 敏感一般数据 | 可用于精准诈骗、骚扰、社工攻击,常与身份证号、地址组合使用。 |
| 地址、固定电话、手机号 | 个人位置与联系方式 | 敏感一般数据 | 组合后可构建完整个人画像,用于线下骚扰或精准营销。 |
| 统一社会信用代码 | 企业唯一标识 | 敏感一般数据(部分场景为重要数据) | 可用于企业冒名注册、税务欺诈、供应链攻击。 |
| 身份证号码 | 个人法定身份标识 | 敏感一般数据(高敏感) | 黑灰产中威胁等级最高,常用于“开盒”、贷款诈骗、身份盗用。 |
| 未成年人身份证号 | 特殊群体身份信息 | 敏感一般数据(高敏感) | 法律重点保护对象,泄露后果更严重,可能触发监管处罚。 |
| 车牌号 | 个人财产与行踪标识 | 敏感一般数据 | 可关联车主身份、行驶轨迹,用于跟踪、勒索或车辆盗抢。 |
| 银行卡号 | 金融账户标识 | 敏感一般数据(高敏感) | 与密码、CVV组合可直接盗刷资金,是黑灰产核心目标。 |
| 电子邮箱 | 网络身份标识 | 敏感一般数据 | 常用于钓鱼、撞库、重置密码,是攻击入口之一。 |
| 姓名 + 手机号 + 身份证 | 个人身份三要素 | 敏感一般数据(高敏感) | 三者组合可完成绝大多数实名认证,是“精准诈骗”的基础。 |
| L1(常规一般数据) | 泄露后不会对个人权益、组织权益造成危害,多为公开或脱敏数据。 | 产品名称、公开地址、非敏感业务描述 |
| L2(低敏感一般数据) | 泄露后可能造成轻微影响,如骚扰电话、营销干扰。 | 手机号(单独)、电子邮箱、固定电话 |
| L3(敏感一般数据) | 泄露后可能导致身份盗用、资金损失、精准诈骗等中等以上损害。 | 身份证号、银行卡号、密码、车牌号、未成年人信息、统一社会信用代码 |
| L4(重要/核心数据) | 泄露后可能引发大规模社会风险、行业动荡或国家安全威胁。 | 国家人口库、金融交易流水、生物识别原始数据、关键基础设施配置 |
- 密码 → L3(高敏感),部分金融系统视为L4;
- 手机号 → L2(单独存在时),若与身份证、地址组合则升级为L3;
- 地址 + 固定电话 + 手机号 → L3(组合后可构建完整画像);
- 统一社会信用代码 → L3(企业级敏感数据,部分场景为L4);
- 身份证号码 → L3(高敏感),未成年人身份证号同样为L3,但法律要求更严格;
- 车牌号 → L3(可关联行踪与财产);
- 银行卡号 → L3(高敏感),与CVV、密码组合即为L4;
- 姓名 + 手机号 + 身份证 → L3(三要素组合,是实名认证基础,黑灰产核心目标);
- 电子邮箱 → L2(单独),若用于重置密码或钓鱼攻击则为L3。
CVV,全称是 Card Verification Value,中文常称为“信用卡安全码”或“卡片验证码”,是印在信用卡背面签名栏末端的一组3位或4位数字。它并非银行卡号的一部分,而是由发卡银行通过加密算法,结合卡号、有效期等信息生成的动态验证值,主要用于在非面对面交易(如网购、电话支付)中确认持卡人确实持有实体卡片,从而防止盗刷和欺诈。
CVV 的核心作用是作为一道独立于密码和卡号的“第二道防线”。即使攻击者窃取了你的银行卡号和有效期,若没有 CVV,绝大多数线上支付平台会拒绝交易。因此,它在金融安全体系中属于最高敏感级别的数据,与密码、磁条数据一样,被严格禁止存储或记录在系统中。
在很多国际支付场景(如 Visa/Mastercard 的境外网站)中,只要拥有“卡号 + 有效期 + CVV”这三样信息,就可以直接扣款,无需短信验证码。因此,CVV 本身就被视为具备直接造成资金损失能力的核心数据。
根据国内《个人金融信息保护技术规范》及国际 PCI DSS 标准,CVV 被归类为 C3 级(最高敏感等级)或 L4 级数据,其安全防护要求极为严苛:
- 严禁以任何形式明文存储;
- 不得出现在日志、调试信息或第三方组件的输出中;
- 传输过程必须加密;
- 商户和系统端无权收集或保存,仅支付机构或银行可在交易瞬间临时验证。(即使加密存储也是违规的)
只要涉及 CVV、磁道数据、支付密码 等直接用于资金交易的鉴权信息,都应毫不犹豫地将其定级为 L4,并采取最严格的加密传输、禁止落盘存储等防护措施。
需要注意的是,分级不是静态的,它会随着数据组合、使用场景、行业属性动态调整。例如:
- 单独的手机号是L2,但若与身份证号、住址组合,即构成“个人身份三要素”,应定为L3;
- 企业在内部系统中存储的“统一社会信用代码”可能是L3,但若用于跨境数据传输或涉及国家经济命脉行业,则可能升级为L4;
- 医疗系统中的“诊断结果”属于L3,但若涉及大规模人群健康数据,则可能被认定为L4。
个人身份信息三要素通常指姓名、身份证号码和手机号码这三项核心信息,它们是当前国内实名认证、金融开户、网络注册等场景中用于验证用户真实身份的基础组合。
三、为什么这些字段被标记为“敏感”?
因为这些字段一旦泄露或被滥用,极易导致:
- 个人层面:身份盗用、精准诈骗、骚扰电话、网络暴力;
- 企业层面:客户信任崩塌、监管处罚、品牌声誉受损;
- 社会层面:大规模数据泄露引发公共安全事件(如“社工库”泛滥)。
尤其在金融、政务、医疗等行业,这些数据常被列为“重要数据”或“敏感个人信息”,需采取加密、脱敏、访问控制、审计日志等强化防护措施。
四、如何进一步防护?
📋 常见问题与解决方案
基于“数安之江”专项检查,文档总结了电子政务领域存在的共性问题及改进建议:
- 制度与人员管理:
- 问题:安全制度模板化、应急预案流于形式、安全培训覆盖面不足、对第三方人员缺乏背景调查。
- 对策:结合实际编制制度,落实应急责任人,加强全员(含驻场人员)安全培训,并对关键岗位的供应方人员进行背景审查。
- 数据与权限管理:
- 问题:数据资产底账不清、未开展分类分级、明文展示个人敏感信息、账号权限分配不合理(如三方人员权限过高)、重要账户密码交由第三方保管。
- 对策:梳理数据资产并落实分类分级,对敏感信息进行脱敏或加密,遵循最小权限原则划分账号权限,核心账号密码应由内部人员严格管理。
- 系统建设与运维:
- 问题:缺乏日志审计功能、未定期进行应急演练和攻防演练、存在弱口令、应用存在未授权访问等漏洞。
- 对策:完善系统日志记录与审计,定期组织演练,强制使用强密码策略(如8位以上,含三种字符),并在系统各阶段严格执行渗透测试和漏洞扫描。
此外,文档还提出了“数据安全八准八不准”的行为准则,为单位人员和第三方服务人员划定了明确的安全红线,例如不准将生产数据直接用于测试、不准未经授权删除数据等,以规范日常操作,降低人为风险。
实操
— ==========================================
— 1. 创建测试表并插入明文数据
— ==========================================
CREATE TABLE users (
id INT PRIMARY KEY IDENTITY(1,1),
username NVARCHAR(50),
id_card NVARCHAR(18), — 身份证号 (待加密)
phone NVARCHAR(20) — 手机号 (待脱敏)
);
— 插入几条测试数据
INSERT INTO users (username, id_card, phone) VALUES
('张三', '330101199001011234', '13800001111'),
('李四', '330102199205055678', '13900002222'),
('王五', '330103198812129012', '13700003333');
— 查看当前的明文数据
SELECT * FROM users;
完整逐行详解这段 SQL(SQL Server 语法)
一、整体功能说明
这段脚本分 3 段:
二、分段逐句拆解
第一段:创建测试表 CREATE TABLE
CREATE TABLE users (
id INT PRIMARY KEY IDENTITY(1,1),
username NVARCHAR(50),
id_card NVARCHAR(18), — 身份证号 (待加密)
phone NVARCHAR(20) — 手机号 (待脱敏)
);
- id INT:字段名称 id,类型整数;
- PRIMARY KEY:主键,每条数据唯一标识,不可重复、不能为空;
- IDENTITY(1,1):自增列,第一条数据 id=1,之后每条自动 + 1,无需手动赋值。
- NVARCHAR:支持中文的可变长度字符串,N 代表 Unicode,避免中文乱码;
- 最多存 50 个字符,存放用户名。
第二段:插入测试数据 INSERT
INSERT INTO users (username, id_card, phone) VALUES
('张三', '330101199001011234', '13800001111'),
('李四', '330102199205055678', '13900002222'),
('王五', '330103198812129012', '13700003333');
第三段:查询查看明文数据 SELECT
SELECT * FROM users;
- SELECT *:查询表中所有字段;
- FROM users:从 users 表读取; 执行后会完整输出:id、姓名、完整身份证、完整手机号,无任何隐藏 / 加密处理。
三、结合你公共数据安全培训的风险解读
- 存储层:对 id_card 使用国密 SM4 加密存储;
- 查询层:配置动态脱敏,phone 自动展示为 138****1111;
- 管控层:增加接口审计、数据库操作日志,记录所有人查询、导出行为。
四、补充语法说明
这段是 SQL Server 专用语法:
- IDENTITY(1,1) 自增是 SQL Server 独有;MySQL、Oracle 写法不同;
- NVARCHAR 适配中文政务场景,政务系统通用。
🛡️ 练习一:数据脱敏 (Dynamic Data Masking)
这是最容易上手的一步,给手机号加上脱敏规则。
— 给 phone 列添加脱敏规则:保留前3位和后4位,中间用 **** 代替
ALTER TABLE users
ALTER COLUMN phone ADD MASKED WITH (FUNCTION = 'partial(3,"****",4)');
— 再次查询,你会发现手机号已经变成了 138****1111 这种格式
SELECT * FROM users;
— 【补充知识】如果你想临时取消脱敏(比如你是管理员需要看明文),可以执行:
— GRANT UNMASK TO 你的当前用户名;

1. 脱敏规则确实加上了,但你当前账号拥有 UNMASK 权限
SQL Server 动态脱敏逻辑:
- 给字段加 MASKED 只是定义脱敏模板,不是所有人查询都会自动打码;
- 只有没有 UNMASK 权限的普通用户,查询才会显示 138****1111;
- 管理员、DBA、sa 默认自带 UNMASK 权限,查询直接展示明文,和你截图现象完全一致。
你代码里注释也写了:
— GRANT UNMASK TO 你的当前用户名;
这条语句就是授予「查看明文」权限,你当前登录账号已经被授予该权限,所以看不到脱敏效果。
2. 完整分步拆解你这段代码
— 给phone列绑定脱敏函数:前3位保留、中间4星、末尾4位
ALTER TABLE users
ALTER COLUMN phone ADD MASKED WITH (FUNCTION = 'partial(3,"****",4)');
- partial(a,掩码,b):显示前 a 个字符 + 中间掩码 + 最后 b 个字符
- 手机号 11 位:前 3 + **** + 后 4 = 138****1111,规则本身没问题,执行无报错说明规则创建成功。
SELECT * FROM users;
你当前账号有权限查看明文,所以结果还是完整手机号。
二、两种验证脱敏效果的方法
方法 1:新建一个普通测试账号,无 UNMASK 权限
— 1. 创建测试用户
CREATE USER test_user WITHOUT LOGIN;
— 2. 授予查询表权限
GRANT SELECT ON users TO test_user;
— 3. 切换身份查询,能看到脱敏效果
EXECUTE AS USER = 'test_user';
SELECT * FROM users;
— 切回原账号
REVERT;
执行后 phone 列就会变成 138****1111。

方法 2:收回当前账号的明文查看权限(不建议生产管理员操作)
REVOKE UNMASK FROM [你的登录账号名];
收回后再 SELECT * FROM users,就自动脱敏; 需要看明文时再执行:
GRANT UNMASK FROM [你的登录账号名];
🔐 练习二:数据加密 (Always Encrypted)
注意: 这个功能在 SSMS 里必须通过向导界面操作,不能直接用 SQL 语句一键完成。
(我当前连接的是 Azure SQL Database 或者是一个较新版本的 SQL Server,它的菜单结构变了,把“Always Encrypted”向导移到了别的地方,或者直接不支持这种老式的图形化向导操作了。所以这次还是使用代码操作)
实操步骤:
执行完毕后,你再去执行 SELECT * FROM users;,会发现 id_card 列的数据变成了一串乱码!这就说明加密成功了。
第一步:基于证书创建一个对称密钥
— 1. 如果还没有数据库主密钥,先创建一个(通常都有,没有再开)
IF NOT EXISTS (SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MyStrongPassword123!';
GO
— 2. 创建一个证书
CREATE CERTIFICATE UserCert WITH SUBJECT = 'User Data Protection';
GO
— 3. 基于证书创建一个对称密钥 (AES_256 算法)
CREATE SYMMETRIC KEY UserKey
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE UserCert;
GO
完整逐行详解(SQL Server 列加密 / 存储加密整套流程,对应你培训里数据加密安全能力,用来加密身份证这类 L4 高敏感字段)
整体作用
这三段 SQL 是搭建数据库对称加密环境,用来把身份证号这类核心敏感数据加密存库(底层不再保存明文,和刚才手机号「查询脱敏」完全是两种安全方案)。 整套链路:数据库主密钥 → 证书 → 对称密钥,三层加密保护加密密钥本身,防止密钥泄露。
1. 第一段:判断并创建【数据库主密钥 Database Master Key】
IF NOT EXISTS (SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MyStrongPassword123!';
GO
逐句拆解
- 数据库主密钥(DMK):当前数据库所有加密体系的根钥匙,是整套加密的起点;
- 作用:用来加密后面创建的证书、对称密钥;
- 必须设置高强度密码,生产环境不能写死明文在脚本里。
业务理解
没有主密钥,后面证书、加密密钥全都建不了;相当于你家里保险箱的总钥匙。
2. 第二段:创建加密证书 Certificate
CREATE CERTIFICATE UserCert WITH SUBJECT = 'User Data Protection';
GO
行业规范:非对称加密(证书)用来保护密钥,对称加密(AES)用来加密业务数据,兼顾安全和性能。
3. 第三段:创建业务对称密钥 UserKey(真正加密身份证的工具)
CREATE SYMMETRIC KEY UserKey
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE UserCert;
GO
政务等等保合规场景会替换为国密 SM4 算法。
整套加密层级关系(从上到下)
和之前手机号脱敏做对比(核心区分两大安全能力)
| 存储层 | 数据库里存密文,无原始明文 | 数据库底层依旧存完整明文 |
| 生效时机 | 写入数据时加密,读取时解密 | 仅查询展示时隐藏,底层不变 |
| 适用数据 | 身份证、银行卡(L4 最高敏感) | 手机号、住址(L3 普通敏感) |
| 泄露风险 | 就算数据库被拖库,拿到密文无法还原 | 拖库后直接拿到完整原始数据 |
| 权限管控 | 必须打开密钥才能解密查看明文 | 仅区分 UNMASK 权限控制展示 |
第二步:修改表结构
原来的 id_card 存的是明文(字符串),加密后变成乱码(二进制),所以我们需要加一列来存密文。
— 添加一个新列用来存加密后的数据 (varbinary 类型)
ALTER TABLE users ADD id_card_encrypted VARBINARY(256);
GO

第三步:执行加密 (把明文变密文)
这一步就是真正的“加密”动作了!
— 打开刚才创建的钥匙
OPEN SYMMETRIC KEY UserKey DECRYPTION BY CERTIFICATE UserCert;
— 更新数据:把 id_card 的内容加密后写入新列
UPDATE users
SET id_card_encrypted = EncryptByKey(Key_GUID('UserKey'), id_card);
GO
— 用完记得关钥匙
CLOSE SYMMETRIC KEY UserKey;
GO
可能出现的问题:权限不足

解决办法:🔍 怎么找到并确认你的账号身份?
— 查看当前连接的“登录名”和“数据库用户”
SELECT
SYSTEM_USER AS '当前登录名(Login)',
USER_NAME() AS '当前数据库用户(User)',
IS_SRVROLEMEMBER('sysadmin') AS '是否是管理员(1是0否)';
- 如果 当前数据库用户 显示的是 test:那就实锤了!你现在就是那个低权限用户,所以刚才报“无法查找密钥”的错。
- 如果 是否是管理员 显示为 0:说明你现在的权限确实不够,必须切换。

🔄 如何切换回管理员账号?
既然你在左侧看不到 test,说明你可能是用 Windows 认证直接连进来的,或者之前的连接缓存了。请按以下步骤强制切换:
- 确保“身份验证”选择的是 SQL Server 身份验证。
- 登录名输入:sa
- 密码:输入你安装 SQL Server 时设置的 sa 密码(如果你没改过,通常是安装时设的那个强密码)。
💡 为什么一定要切回 sa?
因为创建密钥 (Create Key) 和 修改表结构 (Alter Table) 是只有“房东”(管理员)才能干的事。“租客”(test 用户)只能负责“住”(查询数据)。
等你切回 sa 账号,把刚才那段加密代码跑通,看到了 0x… 的密文,咱们再切回 test 账号去测试“解密”,那样你就能亲眼看到:同样的表,管理员看是明文/密文,租客看全是乱码或星号,这个对比效果非常棒!

那如果显示的是1,也就是已经是管理员权限了呢?

刚才报错说“找不到密钥”,那原因就只有一个了:钥匙还没造出来,或者造的时候报错了导致没成功。
别管那个 test 用户了,咱们直接用现在这个“超级管理员”身份,把钥匙造出来,把数据加密了。
🚀 立即执行:一键生成密钥并加密
请直接复制下面这段代码,在当前的查询窗口里全部选中,然后点“执行”。这段代码我加了“防呆设计”,如果之前有残留会先清理,确保能跑通:
— ==========================================
— 第一步:清理旧环境 & 创建新钥匙
— ==========================================
— 1. 如果之前有同名的密钥,先删掉(防止重名报错)
IF EXISTS (SELECT * FROM sys.symmetric_keys WHERE name = 'UserKey')
DROP SYMMETRIC KEY UserKey;
GO
— 2. 如果之前有同名的证书,先删掉
IF EXISTS (SELECT * FROM sys.certificates WHERE name = 'UserCert')
DROP CERTIFICATE UserCert;
GO
— 3. 创建证书
CREATE CERTIFICATE UserCert WITH SUBJECT = 'User Data Protection';
GO
— 4. 创建对称密钥 (这是真正的加密核心)
CREATE SYMMETRIC KEY UserKey
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE UserCert;
GO
— ==========================================
— 第二步:准备存密文的列
— ==========================================
— 检查表里有没有 id_card_encrypted 这一列,没有就加上
IF COL_LENGTH('users', 'id_card_encrypted') IS NULL
BEGIN
ALTER TABLE users ADD id_card_encrypted VARBINARY(256);
PRINT '✅ 新列 id_card_encrypted 创建成功';
END
ELSE
BEGIN
PRINT '⚠️ 列已存在,直接进行下一步';
END
GO
— ==========================================
— 第三步:打开钥匙,执行加密!
— ==========================================
— 1. 打开钥匙
OPEN SYMMETRIC KEY UserKey DECRYPTION BY CERTIFICATE UserCert;
— 2. 更新数据:把明文(id_card)加密后写入新列(id_card_encrypted)
UPDATE users
SET id_card_encrypted = EncryptByKey(Key_GUID('UserKey'), id_card);
— 3. 用完随手关门(安全好习惯)
CLOSE SYMMETRIC KEY UserKey;
GO
— ==========================================
— 第四步:验收成果
— ==========================================
SELECT
username,
id_card AS '原始明文',
id_card_encrypted AS '加密后的密文(乱码)'
FROM users;
💡 预期结果
执行完后,下方的结果网格应该会出现三列:

第四步:验证成果
现在你可以查询看看效果:
— 查看对比
SELECT
username,
id_card AS '原始明文',
id_card_encrypted AS '加密后的密文'
FROM users;
你会看到 id_card_encrypted 列是一串 0x… 开头的乱码,这就代表加密成功了!

数据加密能力
核心定义:敏感数据在存储、传输全过程用密码算法加密,防泄露、篡改、冒充、抵赖,政务场景强制优先使用国密 SM 系列算法,配套合规要求。 整体四层合规链条:密码算法合规 → 密码技术合规 → 商用密码产品认证 → 密码服务许可。
1、四大安全威胁、对应安全目标、加密技术、国密 / 国际算法对照表
|
窃听(秘密泄露) |
数据被偷看到明文,比如数据库拖库、传输抓包 | 机密性 | 对称密码(加密存 / 传数据) | SM1、SM4、ZUC、SM7 | AES、DES、3DES |
| 否认(事后抵赖) | 做完操作不认账,比如泄露后说不是自己导出数据 | 不可否认性 | 公钥密码(数字签名、证书) | SM2、SM9 | RSA、ECC |
|
篡改(信息被修改) |
数据中途被篡改,比如身份证、金额被改 | 完整性 | 单向散列函数 | SM3 | MD5、SHA |
| 伪装(冒充发送方) | 黑客伪造接口、账号冒充合法人员访问 | 身份认证 | MAC/HMAC 消息验证码 | 搭配 SM3 实现 | MD5-HMAC、SHA-HMAC |
2、三类密码技术通俗解释(结合刚写的 SQL 加密代码)
(1)对称密码(SM4/AES)
- 作用:加密业务敏感数据(身份证、手机号存储加密),解决「窃听、机密性」;
- 对应代码:AES_256 就是国际对称算法;政务要替换成国密 SM4;
- 特点:加密、解密用同一把密钥,速度快,适合大批量业务数据加密。
(2)公钥密码(SM2/RSA)
- 作用:做证书、数字签名,解决「抵赖、身份认证」;
- 对应代码:脚本里的CERTIFICATE证书就是基于非对称公钥密码;
- 特点:一对公私钥,公钥加密、私钥解密,用来保护对称密钥、做操作签名留痕溯源。
(3)单向散列(SM3/SHA)
- 作用:校验数据有没有被篡改,不可逆(不能还原原文);
- 场景:密码哈希存储、接口数据完整性校验;
- 特点:原文改一个字符,哈希值完全变化,无法反向算出原始数据。
3、和刚才两段 SQL 代码对应联动(打通实操 + 理论)
- AES256 = PPT 里国际对称算法;政务合规替换为国密 SM4;
- CERTIFICATE 证书 = PPT 公钥密码技术(SM2 对应国密证书);
- 用途:实现存储加密,解决「窃听、数据泄露」,对应 PPT 第一条威胁防护。
- 不属于加密能力!只是前端展示隐藏,底层数据仍是明文;
- 「数据加密能力」特指存储 / 传输底层密文防护,脱敏是配套辅助能力,二者不能互相替代。
🔑 练习三:权限管控 (Permission Control)
你可以建一个普通账号,测试一下他是不是真的看不到敏感数据。
— 1. 创建一个专门用来测试的普通登录账号
CREATE LOGIN test_user WITH PASSWORD = 'Test@123456';
— 2. 在当前数据库下为这个登录名创建一个用户
CREATE USER test_user FOR LOGIN test_user;
— 3. 给这个用户赋予查询 users 表的权限
GRANT SELECT ON users TO test_user;
— 4. 测试权限(你可以右键点击数据库,选择“更改连接”,用 test_user 的身份连进去查一下)
— 你会发现,test_user 查出来的数据是脱敏后的,且无法执行增删改操作。
— 5. 练习清理(练完手后,把测试账号删掉)
— REVOKE SELECT ON users TO test_user;
— DROP USER test_user;
— DROP LOGIN test_user;

— ==========================================
— 1. 给已经存在的 test_user2 补发“查看权限”
— ==========================================
GRANT SELECT ON dbo.Users TO test_user2;
GO
— ==========================================
— 2. 验证权限是否生效
— ==========================================
— 模拟 test_user2 的身份来查询(不用切换窗口,直接用这句命令)
EXECUTE AS USER = 'test_user2';
SELECT
SYSTEM_USER AS '当前模拟登录名',
USER_NAME() AS '当前数据库用户',
*
FROM dbo.Users;
REVERT; — 恢复回 sa 身份
GO

练习四:添加水印
💧 玩法一:查询层水印(最简单,适合导出溯源)
这种水印是在数据导出或查询时,动态拼接上去的。比如你要把用户数据导出给第三方公司,可以在数据里偷偷加一个“追踪 ID”。
实操代码: 假设你要把数据给“A公司”,可以执行以下查询:
— 在查询结果中动态附加一个隐藏的水印标识
SELECT
username,
id_card,
phone,
'WATERMARK_FOR_COMPANY_A_20260713' AS trace_id — 这就是你的水印
FROM dbo.Users;
效果:如果未来这批数据泄露了,你看到数据里带有 COMPANY_A 的标识,就能立刻锁定是提供给 A 公司的那批数据出了问题。
🧬 玩法二:存储层水印(进阶,适合防篡改)
这种水印是直接把信息“藏”进数据库的表结构里。我们可以利用 SQL Server 的默认值来实现。
实操代码: 我们可以给 users 表加一个默认带水印的列。
— 1. 给表增加一个专门存水印的列(默认值为当前数据库用户或特定标识)
ALTER TABLE dbo.Users
ADD data_watermark NVARCHAR(100) DEFAULT (SYSTEM_USER + '_DB_TRACE');
GO
— 2. 查看效果(你会发现新插入的数据自动带上了水印)
SELECT * FROM dbo.Users;
效果:即使黑客拖走了数据库,或者内部人员偷偷复制了数据,只要数据还在,这个 data_watermark 列就会像幽灵一样跟着,证明这批数据是从哪个环境、哪个账号流出去的。

🧐 为什么是 NULL?
SQL Server 的 DEFAULT(默认值)机制有个特点:它只在“新插入”数据时生效。 对于表里原本就存在的“老数据”(张三、李四、王五),数据库不会自动回头去给它们补填这个默认值,所以它们依然显示为空(NULL)。
🛠️ 怎么让老数据也带上水印?
只需要执行一条简单的 UPDATE 语句,强制刷新一下这几行数据即可。
请在当前窗口继续执行以下代码:
— 1. 刷新老数据:把 watermark 列更新为默认值
— 这里的逻辑是:只要这一行是 NULL,就把它更新为系统生成的默认值
UPDATE dbo.Users
SET data_watermark = DEFAULT
WHERE data_watermark IS NULL;
— 2. 再次查看效果
SELECT * FROM dbo.Users;

💡 预期结果
执行完上面的 UPDATE 后,你应该会看到 data_watermark 这一列变成了类似这样的内容:
- sa_DB_TRACE (因为你现在是用 sa 账号在执行更新操作)
🚀 下一步验证(见证奇迹的时刻)
为了让这个水印真正发挥作用,你可以试着插入一条新数据,看看它是不是自动就带上了水印,而不需要手动去填:
— 3. 测试插入新数据(故意不写 watermark 列)
INSERT INTO dbo.Users (username, id_card, phone)
VALUES ('赵六', '330106200001019999', '15900006666');
— 4. 查看最终结果
SELECT * FROM dbo.Users;

⚠️ 发现一个小盲点
请注意看“赵六”那一行的 id_card_encrypted 列(倒数第二列),它是 NULL。
这是因为我们刚才只给表加了“水印列”的默认值,但没给“加密列”加。如果业务上要求所有入库的身份证必须加密,光靠应用层代码是不够的(万一有人绕过代码直接插库呢?)。
我们可以把这个“加密动作”也自动化,做成一个触发器 (Trigger)。这样无论谁、用什么方式插数据,身份证号都会自动变密文。
🛡️ 自动加密触发器代码
— ==========================================
— 1. 创建触发器: trg_Encrypt_IDCard
— 作用:当有新数据插入或更新时,自动把 id_card 加密写入 id_card_encrypted
— ==========================================
CREATE TRIGGER trg_Encrypt_IDCard
ON dbo.Users
AFTER INSERT, UPDATE
AS
BEGIN
— 关闭影响行数提示,防止干扰程序
SET NOCOUNT ON;
— 检查是否开启了密钥(必须打开才能加密)
IF NOT EXISTS (SELECT * FROM sys.symmetric_keys WHERE name = 'SymKey_Users' AND is_open = 1)
BEGIN
OPEN SYMMETRIC KEY SymKey_Users DECRYPTION BY CERTIFICATE Cert_Users;
END
— 核心逻辑:更新刚刚插入/修改的那几行数据
UPDATE u
SET u.id_card_encrypted = EncryptByKey(Key_GUID('SymKey_Users'), i.id_card)
FROM dbo.Users u
INNER JOIN inserted i ON u.id = i.id;
END;
GO
PRINT '✅ 触发器创建成功!以后插入数据会自动加密。';

这是一个典型的“假报错”现象。
但是!如果现在直接去插入数据,触发器会在运行时再次报错并导致插入失败。 我们需要把这个“有缺陷”的触发器删掉,换一个绝对稳健的版本。
🛠️ 修复方案:使用“先开后用”策略
在触发器里去“检查”密钥是否打开非常麻烦且容易出错。最简单的做法是:不管它开没开,每次进来都强制打开一次。SQL Server 很聪明,如果密钥已经开了,重复执行 OPEN 是不会报错的。
请复制下面这段修正版代码,它会先删除旧触发器,再创建新的:
— ==========================================
— 1. 删除刚才那个有瑕疵的触发器
— ==========================================
IF OBJECT_ID('trg_Encrypt_IDCard', 'TR') IS NOT NULL
DROP TRIGGER trg_Encrypt_IDCard;
GO
— ==========================================
— 2. 创建修正后的触发器 (稳健版)
— ==========================================
CREATE TRIGGER trg_Encrypt_IDCard
ON dbo.Users
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
— 【核心修改】不再检查状态,直接强制打开密钥
— 如果密钥已经打开,这句不会报错;如果没开,这句会打开它。
OPEN SYMMETRIC KEY SymKey_Users DECRYPTION BY CERTIFICATE Cert_Users;
— 执行加密更新
UPDATE u
SET u.id_card_encrypted = EncryptByKey(Key_GUID('SymKey_Users'), i.id_card)
FROM dbo.Users u
INNER JOIN inserted i ON u.id = i.id;
END;
GO
PRINT '✅ 修正版触发器已就绪!';

🧪 验证测试(见证奇迹的时刻)
现在我们来测试一下。为了证明触发器生效了,我们先删掉刚才那个没加密的“赵六”,然后重新插入一个“赵六”。
— 1. 删除旧的“赵六”(那个加密列是 NULL 的)
DELETE FROM dbo.Users WHERE username = '赵六';
— 2. 重新插入“赵六”(这次故意只写明文身份证)
INSERT INTO dbo.Users (username, id_card, phone)
VALUES ('赵六', '330106200001019999', '15900006666');
— 3. 查看结果
SELECT * FROM dbo.Users WHERE username = '赵六';
可能出现的问题:

为什么会报错?
报错信息说:无法对对称密钥 'SymKey_Users' 执行查找,因为它不存在…
原因很简单:“钥匙”还没拿出来。 在 SQL Server 中,加密用的对称密钥默认是关闭的。每次打开一个新的查询窗口(Query Window),或者重启服务后,你都必须先手动执行一句 OPEN SYMMETRIC KEY,数据库才知道怎么用它来加密数据。
刚才我们删掉了“赵六”,现在重新插入时,触发了自动加密逻辑,但因为你在这个新窗口里还没把密钥“打开”,所以触发器拿着明文身份证想去加密时,发现手里没钥匙,就报错了。
🛠️ 解决方法(只需两步)
请在当前窗口的最上方,加上打开密钥的代码,然后再执行插入操作。
第一步:打开密钥(必做!)
— 必须先执行这句,告诉数据库把保险箱打开
OPEN SYMMETRIC KEY SymKey_Users
DECRYPTION BY CERTIFICATE Cert_Users;
GO
如果执行这个出现报错:

这意味着:在这个数据库里,根本就没有叫 SymKey_Users 的对称密钥。之前的触发器之所以能创建成功,是因为创建触发器时不会检查密钥是否存在,只有真正执行加密动作时才会发现“查无此钥”。
看来我们需要从头把“加密基础设施”搭建起来。请按照以下顺序,一步步执行代码:
🛠️ 第一步:确认并创建证书
对称密钥必须依附于一个证书。我们先检查一下证书在不在,如果不在就创建一个。
— 检查证书是否存在
IF NOT EXISTS (SELECT * FROM sys.certificates WHERE name = 'Cert_Users')
BEGIN
CREATE CERTIFICATE Cert_Users WITH SUBJECT = 'User Data Protection';
PRINT '✅ 证书 Cert_Users 创建成功';
END
ELSE
PRINT 'ℹ️ 证书 Cert_Users 已存在';
GO

🔑 第二步:创建对称密钥
这是核心步骤。我们将创建一个名为 SymKey_Users 的密钥,并用刚才的证书保护它。
— 检查密钥是否存在
IF NOT EXISTS (SELECT * FROM sys.symmetric_keys WHERE name = 'SymKey_Users')
BEGIN
CREATE SYMMETRIC KEY SymKey_Users
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE Cert_Users;
PRINT '✅ 对称密钥 SymKey_Users 创建成功';
END
ELSE
PRINT 'ℹ️ 对称密钥 SymKey_Users 已存在';
GO

🔄 第三步:重建触发器
既然密钥刚刚被我们“生”出来了,为了确保万无一失,我们把之前的触发器删掉重建一下(防止之前的缓存问题)。
— 删除旧触发器
IF OBJECT_ID('trg_Encrypt_IDCard', 'TR') IS NOT NULL
DROP TRIGGER trg_Encrypt_IDCard;
GO
— 创建新触发器
CREATE TRIGGER trg_Encrypt_IDCard
ON dbo.Users
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
— 强制打开密钥(现在它确实存在了)
OPEN SYMMETRIC KEY SymKey_Users DECRYPTION BY CERTIFICATE Cert_Users;
— 执行加密更新
UPDATE u
SET u.id_card_encrypted = EncryptByKey(Key_GUID('SymKey_Users'), i.id_card)
FROM dbo.Users u
INNER JOIN inserted i ON u.id = i.id;
END;
GO
PRINT '✅ 触发器重建完成,基础设施就绪!';

🧪 第四步:最终验证
现在环境已经干净了,我们可以重新测试“赵六”的插入。
— 1. 清理旧数据
DELETE FROM dbo.Users WHERE username = '赵六';
— 2. 插入新数据(测试自动加密)
INSERT INTO dbo.Users (username, id_card, phone)
VALUES ('赵六', '330106200001019999', '15900006666');
— 3. 查看结果
SELECT * FROM dbo.Users WHERE username = '赵六';

💡 为什么之前会这样?
很可能是因为在之前的某个环节,创建密钥的代码没有执行成功(比如被跳过了,或者在另一个窗口执行了但没提交/没同步过来),导致表结构和触发器都建好了,但底层的“钥匙”却丢了。
💡 预期结果
执行完上面的查询后,你会发现“赵六”这一行的 id_card_encrypted 不再是 NULL,而是一串以 0x00… 开头的乱码密文了!
这就意味着,你的表现在拥有了双重自动化防御:




