触发器不能防范SQL注入,反而是高危攻击跳板;因其以定义者权限执行动态SQL,低权限用户可通过恶意输入触发高危操作,安全做法是禁用非必要触发器、改用带白名单校验的存储过程。

触发器不能防范SQL注入——它本身就是SQL注入高危载体,尤其在低权限用户可写入的表上定义触发器时,反而会放大风险。
为什么触发器不是防御手段,而是攻击跳板
MySQL触发器以定义者权限执行,不继承调用者权限。这意味着:哪怕一个普通用户只有 INSERT 权限,只要他能往某张表(如 logs)写入数据,而该表上存在一个用动态SQL拼接的触发器,攻击者就能通过构造恶意输入,让触发器代为执行高权限操作。
- 典型错误模式:
AFTER INSERT ON logs触发器里用CONCAT()拼接inserted.payload并EXECUTE IMMEDIATE(或PREPARE/EXECUTE) - 后果:用户插入
'1'; DROP TABLE users; --,触发器就真删表 - MySQL 8.0 不支持在触发器中使用
PREPARE+ 参数化语句,所有动态执行都必须拼字符串
真正有效的防护路径:从源头掐断写入通道
防范低权限用户的恶意写入,核心是控制“谁能把什么数据写进哪张表”,而不是指望触发器做校验或过滤。
- 删除或禁用所有非必需的触发器,尤其是涉及
EXECUTE、PREPARE、SELECT ... INTO @var的 - 对可被低权限用户写入的表(如
logs、feedback),显式收回INSERT权限,改用存储过程封装写入逻辑 - 存储过程中用
VALIDATE_PASSWORD_STRENGTH()或正则REGEXP_LIKE()做白名单校验(例如只允许字母、数字、下划线、短横线) - 设置列级权限:对敏感字段(如
payload)禁止低权限用户INSERT或UPDATE,仅允许应用专用账号操作
如果必须用触发器记录日志,该怎么写才安全
唯一安全的触发器场景,是静态、无条件、无动态SQL的日志记录。它不解析、不转发、不执行任何来自 inserted 或 deleted 的内容。
- ✅ 安全示例:
INSERT INTO audit_log (table_name, op, user, ts) VALUES ('users', 'UPDATE', USER(), NOW()) - ❌ 危险示例:
INSERT INTO audit_log (detail) VALUES (CONCAT('user=', NEW.username, ', email=', NEW.email))—— 若NEW.email含单引号或分号,可能破坏语句结构 - MySQL 8.0 中,
NEW.*和OLD.*字段值不可参数化,所以任何拼接都等同于裸字符串注入 - 若需记录原始输入,应直接
INSERT为 BLOB 或 TEXT 类型,不做任何解释性处理
最常被忽略的一点:触发器里的 USER() 返回的是定义者身份,不是调用者;而 CURRENT_USER() 才反映实际连接用户。但即便用了 CURRENT_USER(),也改变不了触发器本身执行动态SQL的风险本质——它不是防火墙,是火药桶的引信。


















