在MySQL中对敏感字段进行加密、解密及脱敏处理
在处理数据库中的敏感信息时,如身份证号码、电话号码等,确保这些数据的安全性至关重要。本文将介绍如何使用MySQL触发器和内置函数(如AES加密/解密和Base64编码/解码)来实现对特定字段的加密、解密以及脱敏处理,同时保持表结构不变。
背景
直接存储明文敏感信息存在安全风险。为了解决这个问题,我们可以采用加密技术保护这些数据。然而,AES加密后的结果是二进制数据,不适合存储在VARCHAR
类型的列中。因此,我们将使用Base64编码将加密后的二进制数据转换成文本格式,以便存储。
表结构
我们以一个名为ucam_person_account_info
的表为例,该表包含多个可能需要加密或脱敏处理的字段:
CREATE TABLE `ucam_person_account_info` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`depositor_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '存款人姓名',
`depositor_masked_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '脱敏存款人姓名',
`depositor_id_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '存款人身份证件种类',
`depositor_id_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '存款人身份证件号码',
`depositor_id_masked_number` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '脱敏身份证件号码',
`id_expiry_date` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '身份证件到期日',
`issuing_authority_region_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '发证机关所在地的地区代码',
`depositor_nationality_region` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '存款人国籍/地区',
`depositor_gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '存款人性别',
`birth_year` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '出生年份',
`depositor_phone_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '存款人电话',
`depositor_masked_phone_number` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '脱敏存款人电话',
`is_opened_agent` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否代理开户',
`account_bank_institution_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开户银行金融机构编码',
`account_number` varchar(35) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '账号唯一',
`account_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '账户类型',
`linked_type_i_account_number` varchar(35) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '绑定I类账户账号',
`linked_type_i_account_bank_institution_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '绑定I类账户开户银行金融机构编码',
`account_opening_date` datetime DEFAULT NULL COMMENT '开户日期',
`account_closing_date` datetime DEFAULT NULL COMMENT '销户日期',
`account_status` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '账户状态',
`currency_type` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '币种类型',
`currency` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '币种',
`special_category_account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '特殊种类账户',
`account_information_type` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '信息类型',
`account_opening_channel` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开户渠道',
`remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`is_joint_account` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否为“联名账户”',
`account_opening_region_code` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开户地地区代码',
`reserved_field1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '预留字段1',
`reserved_field2` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '预留字段2',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT '备注',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='个人账户信息表';
准备工作
首先,我们需要设置一个加密密钥,并使用SHA-256哈希算法生成它:
SET @encryption_key = SHA2('your_secret_key', 256); -- 替换为你的密钥
创建触发器
加密触发器
在插入或更新记录时,我们将使用AES加密结合Base64编码来加密敏感字段:
DELIMITER $$
CREATE TRIGGER before_insert_encrypt_sensitive_data
BEFORE INSERT ON ucam_person_account_info FOR EACH ROW
BEGIN
IF NEW.depositor_id_number IS NOT NULL THEN
SET NEW.depositor_id_number = TO_BASE64(AES_ENCRYPT(NEW.depositor_id_number, @encryption_key));
END IF;
IF NEW.depositor_phone_number IS NOT NULL THEN
SET NEW.depositor_phone_number = TO_BASE64(AES_ENCRYPT(NEW.depositor_phone_number, @encryption_key));
END IF;
END$$
CREATE TRIGGER before_update_encrypt_sensitive_data
BEFORE UPDATE ON ucam_person_account_info FOR EACH ROW
BEGIN
IF NEW.depositor_id_number IS NOT NULL THEN
SET NEW.depositor_id_number = TO_BASE64(AES_ENCRYPT(NEW.depositor_id_number, @encryption_key));
END IF;
IF NEW.depositor_phone_number IS NOT NULL THEN
SET NEW.depositor_phone_number = TO_BASE64(AES_ENCRYPT(NEW.depositor_phone_number, @encryption_key));
END IF;
END$$
DELIMITER ;
脱敏触发器
对于需要脱敏显示的字段,在插入或更新时生成相应的脱敏数据:
DELIMITER $$
CREATE TRIGGER before_insert_mask_sensitive_data
BEFORE INSERT ON ucam_person_account_info FOR EACH ROW
BEGIN
IF NEW.depositor_name IS NOT NULL THEN
SET NEW.depositor_masked_name = CONCAT(SUBSTRING(NEW.depositor_name, 1, 1), REPEAT('*', LENGTH(NEW.depositor_name) - 1));
END IF;
IF NEW.depositor_id_number IS NOT NULL THEN
SET NEW.depositor_id_masked_number = CONCAT(SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_id_number), @encryption_key), 1, 6), '****', SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_id_number), @encryption_key), 14));
END IF;
IF NEW.depositor_phone_number IS NOT NULL THEN
SET NEW.depositor_masked_phone_number = CONCAT(SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_phone_number), @encryption_key), 1, 3), '****', SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_phone_number), @encryption_key), 8));
END IF;
END$$
CREATE TRIGGER before_update_mask_sensitive_data
BEFORE UPDATE ON ucam_person_account_info FOR EACH ROW
BEGIN
IF NEW.depositor_name IS NOT NULL THEN
SET NEW.depositor_masked_name = CONCAT(SUBSTRING(NEW.depositor_name, 1, 1), REPEAT('*', LENGTH(NEW.depositor_name) - 1));
END IF;
IF NEW.depositor_id_number IS NOT NULL THEN
SET NEW.depositor_id_masked_number = CONCAT(SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_id_number), @encryption_key), 1, 6), '****', SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_id_number), @encryption_key), 14));
END IF;
IF NEW.depositor_phone_number IS NOT NULL THEN
SET NEW.depositor_masked_phone_number = CONCAT(SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_phone_number), @encryption_key), 1, 3), '****', SUBSTRING(AES_DECRYPT(FROM_BASE64(NEW.depositor_phone_number), @encryption_key), 8));
END IF;
END$$
DELIMITER ;
查询时解密
查询加密的数据时,需要先用FROM_BASE64()
函数将Base64编码的数据转换回二进制形式,然后使用AES_DECRYPT()
解密:
SELECT
id,
CAST(AES_DECRYPT(FROM_BASE64(depositor_id_number), @encryption_key) AS CHAR) AS depositor_id_number,
CAST(AES_DECRYPT(FROM_BASE64(depositor_phone_number), @encryption_key) AS CHAR) AS depositor_phone_number,
depositor_masked_name,
depositor_id_masked_number,
depositor_masked_phone_number,
depositor_name,
account_number,
create_time,
update_time
FROM
ucam_person_account_info;
示例操作
插入新记录
INSERT INTO ucam_person_account_info (
depositor_name,
depositor_id_type,
depositor_id_number,
id_expiry_date,
issuing_authority_region_code,
depositor_nationality_region,
depositor_gender,
birth_year,
depositor_phone_number,
is_opened_agent,
account_bank_institution_code,
account_number,
account_type,
linked_type_i_account_number,
linked_type_i_account_bank_institution_code,
account_opening_date,
account_status,
currency_type,
currency,
special_category_account,
account_information_type,
account_opening_channel,
remarks,
is_joint_account,
account_opening_region_code
) VALUES (
'张三',
'身份证',
'123456789012345678', -- 将被加密并Base64编码
'2025-12-31',
'110000',
'中国',
'男',
'1990',
'13800001234', -- 将被加密并Base64编码
'否',
'BANK001',
'ACC123456789',
'储蓄账户',
'LINKACC123456789',
'BANK002',
NOW(),
'正常',
'CNY',
'人民币',
NULL,
'个人信息',
'线上',
'备注信息',
'否',
'110101'
);
更新记录
UPDATE ucam_person_account_info
SET
depositor_phone_number = '13900005678', -- 新的电话号码(将被加密并Base64编码)
update_time = NOW()
WHERE
id = 1; -- 假设要更新id为1的记录
通过上述步骤,你可以在不修改现有表结构的情况下,利用MySQL的触发器机制对敏感字段进行加密、解密以及脱敏处理,从而增强数据的安全性和隐私保护。