在业务系统中,核心业务表的数据安全性至关重要,为防止数据被非法修改或意外篡改,我们可以通过SQL触发器实现表级别的防篡改校验,核心思路是为每一行数据生成唯一的Hash值,当数据发生变更时自动比对Hash值是否匹配。

实现思路梳理
整体实现分为三个核心步骤:
- 首先为需要保护的表新增一个存储Hash值的字段,用于保存每一行数据的校验标识
- 然后编写计算数据行Hash值的函数,将行内所有核心字段的内容拼接后生成固定长度的Hash串
- 最后创建INSERT和UPDATE触发器,在数据写入和更新时自动计算Hash值并存储,同时在更新时比对原有Hash值是否合法
Hash值计算函数实现
我们以MySQL数据库为例,使用SHA1算法计算Hash值,将行内所有需要参与校验的字段内容拼接后生成Hash串,以下为函数实现代码:
-- 创建计算行Hash值的函数,传入需要参与校验的字段值
DELIMITER //
CREATE FUNCTION calc_row_hash(
p_id INT,
p_name VARCHAR(50),
p_age INT,
p_score DECIMAL(5,2)
)
RETURNS VARCHAR(40)
DETERMINISTIC
BEGIN
-- 将所有字段值拼接后计算SHA1 Hash,字段之间用分隔符区分避免拼接冲突
DECLARE v_hash VARCHAR(40);
SET v_hash = SHA1(CONCAT_WS('|', p_id, p_name, p_age, p_score));
RETURN v_hash;
END //
DELIMITER ;
目标表结构准备
假设我们需要保护的学生成绩表结构如下,其中row_hash字段就是用来存储每一行数据的Hash校验值:
-- 创建学生成绩表
CREATE TABLE student_score (
id INT PRIMARY KEY AUTO_INCREMENT,
student_name VARCHAR(50) NOT NULL,
age INT NOT NULL,
score DECIMAL(5,2) NOT NULL,
row_hash VARCHAR(40) DEFAULT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
触发器创建实现
我们需要创建两个触发器,分别处理INSERT和UPDATE操作,INSERT时自动计算Hash值,UPDATE时先校验原有Hash值是否合法,再更新新的Hash值。
INSERT触发器
插入数据时自动计算当前行的Hash值并存储到row_hash字段:
-- 创建INSERT触发器
DELIMITER //
CREATE TRIGGER trg_student_score_insert
BEFORE INSERT ON student_score
FOR EACH ROW
BEGIN
-- 调用Hash计算函数,为新插入的行生成Hash值
SET NEW.row_hash = calc_row_hash(NEW.id, NEW.student_name, NEW.age, NEW.score);
END //
DELIMITER ;
UPDATE触发器
更新数据时先校验原有行的Hash值是否被篡改,再计算新的Hash值存储:
-- 创建UPDATE触发器
DELIMITER //
CREATE TRIGGER trg_student_score_update
BEFORE UPDATE ON student_score
FOR EACH ROW
BEGIN
-- 校验原有行的Hash值是否匹配,如果不匹配说明数据被非法篡改,抛出异常
DECLARE v_old_hash VARCHAR(40);
-- 这里实际场景中如果是从OLD对象获取,需要注意OLD.id是更新前的id
-- 为简化演示,假设校验逻辑是直接比对OLD的row_hash和重新计算的OLD行Hash
SET v_old_hash = calc_row_hash(OLD.id, OLD.student_name, OLD.age, OLD.score);
IF OLD.row_hash != v_old_hash THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '数据校验失败,当前行已被非法篡改';
END IF;
-- 数据合法,计算更新后的新Hash值
SET NEW.row_hash = calc_row_hash(NEW.id, NEW.student_name, NEW.age, NEW.score);
END //
DELIMITER ;
功能验证
我们可以通过以下操作验证防篡改功能是否生效:
正常插入数据
-- 插入正常数据,触发器会自动生成row_hash
INSERT INTO student_score (student_name, age, score) VALUES ('张三', 18, 92.5);
-- 查询插入的数据,row_hash字段会自动填充
SELECT * FROM student_score;
模拟非法篡改数据
如果直接修改数据库中的score字段而不通过正常业务更新,再次触发更新操作时会校验失败:
-- 模拟非法篡改,直接修改score字段(绕过业务层) UPDATE student_score SET score = 100 WHERE id = 1; -- 此时row_hash还是原来的Hash值,下次正常更新时会触发校验失败 -- 尝试正常更新该行的学生姓名,会触发触发器校验 UPDATE student_score SET student_name = '张三三' WHERE id = 1; -- 上述语句会抛出"数据校验失败,当前行已被非法篡改"的异常
注意事项
- Hash值计算时参与拼接的字段需要根据业务需求选择,非核心字段可以不参与计算减少性能消耗
- 如果表结构发生变更,需要同步修改Hash计算函数和触发器逻辑,避免校验逻辑失效
- 触发器的性能开销需要根据表的写入频率评估,高频写入的表可以考虑异步校验方案
- 如果使用的是其他数据库如PostgreSQL、SQL Server,触发器和Hash函数的语法需要做对应调整,核心逻辑保持一致