SQL触发器无法可靠拦截SQL注入——注入发生在应用层字符串拼接阶段,触发器仅处理已解析的合法值,既无法识别原始恶意SQL,也无法阻止语句构造;真正有效的是应用层参数化查询。

SQL触发器无法可靠拦截SQL注入
直接说结论:你不能靠数据库触发器来防御SQL注入。这不是触发器的设计目标,也做不到真正安全。SQL注入是应用层把用户输入拼接到SQL语句中导致的,等数据进到触发器时,恶意内容已经是“合法字符串”了——触发器看到的只是普通文本,不是待执行的SQL代码。
触发器里能做的只是粗筛可疑字符串
如果你坚持在数据库侧加一层检查(比如遗留系统无法改应用代码),触发器最多对 INSERT 或 UPDATE 的字段值做简单模式匹配,比如检测单引号、分号、UNION、SELECT 等常见关键词。但这类规则极易误杀(比如用户真要存 “It’s a test; please check.”)或绕过(大小写变形、编码混淆、注释符绕过等)。
示例(PostgreSQL):
CREATE OR REPLACE FUNCTION check_sql_injection()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.content ~* '(\b(SELECT|UNION|INSERT|DROP|EXEC|DECLARE)\b)|(;|--|\-\-|/\*|\*/|''|")' THEN
RAISE EXCEPTION 'Possible SQL injection detected in content';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;- 正则中的
\b是单词边界,避免匹配到 “selection” 这类正常词 -
~*表示大小写不敏感匹配,但攻击者可用SeLeCt或%53%45%4c%45%43%54绕过 - 完全无法识别 Base64 编码、Unicode 同形字、或利用 JSON 字段嵌套执行的场景
真正有效的做法只有一条路径
所有参数化查询必须由应用层完成,数据库只接收绑定参数后的纯值。这是唯一被验证有效的方案。
- Python用
cursor.execute("SELECT * FROM users WHERE name = %s", [user_input]),而不是"... WHERE name = '" + user_input + "'" - Java用
PreparedStatement,绝不用Statement拼接 - Node.js用
pg.query('SELECT * FROM x WHERE y = $1', [input]),不用模板字符串直插 - 即使用了ORM(如Django ORM、Sequelize),也要确认它没开启原始SQL拼接开关(如Django的
extra()、raw())
如果非要在数据库加校验,优先考虑CHECK约束而非触发器
CHECK约束更轻量、更声明式,且在语句解析阶段就生效。虽然能力仍有限,但比触发器少一层运行时开销,也更容易被查询优化器感知。
例如 PostgreSQL 中:
ALTER TABLE comments ADD CONSTRAINT no_semicolon CHECK (content !~ ';');
但注意:CHECK 无法调用函数(如正则匹配需用 ~ 运算符),也不能跨字段逻辑判断,更不能抛出带上下文的错误信息——它的作用就是快速拒掉最露骨的输入。
真正难防的是那些看起来完全正常的输入,比如一个看似无害的邮箱地址,却在应用层被错误地拼进动态SQL里。这种问题,数据库连“看见”的机会都没有。

















