
MySQL 8.0+ 用 rewrite_rules 插件拦截危险 SQL
MySQL 原生不支持 SQL 黑名单,但 rewrite_rules 插件(属于 mysql-server 的可选组件)能重写或拒绝匹配的语句——这是最接近“黑名单”的官方方案。它不是在应用层过滤,而是在 parser 阶段介入,对 SELECT、UPDATE、DELETE 等语句生效,但对 SET、SHOW 或存储过程内部语句无效。
常见错误现象:Plugin 'rewriter' is not installed;启用后语句没被拦截,其实是 pattern 匹配失败(比如没转义通配符、大小写不敏感但规则写了大写)。
- 先确认插件可用:
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'rewriter'; - 若未安装,执行:
INSTALL PLUGIN rewriter SONAME 'rewriter.so';(Linux)或'rewriter.dll'(Windows) - 规则必须插入到
query_rewrite.rewrite_rules表,且需调用query_rewrite.flush_rewrite_rules()才生效 - pattern 使用
%和_通配符,但不支持正则;匹配是**大小写不敏感**的,所以写drop%table就能覆盖DROP TABLE和drop table
怎么写一条真正生效的 DELETE 全表禁止规则
想拦住 DELETE FROM users; 这类无条件删全表操作?直接写 DELETE FROM users 不行——因为 MySQL 解析后会补空格、换行,甚至加反引号,实际 pattern 要更宽松。关键是抓住“DELETE + FROM + 表名 + 分号/结尾”这个结构,同时避免误杀带 WHERE 的合法删除。
使用场景:运维临时禁用高危表的无条件删除,防止误操作;不是长期权限替代方案。
- 插入规则:
INSERT INTO query_rewrite.rewrite_rules (pattern, pattern_database, enabled) VALUES ('DELETE FROM users%', 'myapp_db', 'YES'); - 注意
pattern_database必须和客户端连接时指定的 DB 一致,否则不匹配;如果跨库操作,留空或设为NULL - 不要写
DELETE FROM users;——分号不是 parser 输入的一部分,加了反而不匹配 - 生效后,执行该语句会报错:
ERROR 1886 (HY000): Query was rewritten and is now forbidden
rewrite_rules 的硬限制和兼容性坑
这插件不是万能开关。它只作用于客户端发来的原始语句,一旦语句被预处理(如 ORM 拼接、存储过程展开)、或用了变量、PREPARE,就完全绕过。MySQL 5.7 不支持,只有 8.0.12+ 完整可用,且不能拦截 DDL(ALTER、DROP DATABASE)——这些得靠权限系统或代理层。
- 不支持参数化查询中的占位符匹配,
DELETE FROM ?这种写法无法识别表名 - 规则表本身需要
INSERT/SELECT权限,且query_rewrite库默认只有 root 可写 - 性能影响极小(单次字符串前缀匹配),但规则太多(>100 条)可能轻微拖慢 parse 阶段
- 重启 MySQL 后规则不会自动加载,必须手动调用
query_rewrite.flush_rewrite_rules()
比 rewrite_rules 更靠谱的替代方案有哪些
真要防误删、防慢查、防全表扫描,rewrite_rules 只能当辅助。核心防线还是权限控制 + 代理层 + 审计日志。
- 权限层面:用
REVOKE DELETE ON myapp_db.users FROM 'appuser'@'%';最直接;但没法按条件区分(比如只允许带 WHERE 的 DELETE) - 代理层(如 ProxySQL、MaxScale):可在转发前做正则匹配、语句特征提取,支持更复杂的黑白名单逻辑
- 审计插件(
server_audit):不拦截,但能立刻告警 + 记录,配合脚本自动 kill 连接 - 开发侧:所有 DELETE/UPDATE 必须带 WHERE 子句,用
sql_safe_updates=ON强制(但仅对客户端生效,且不阻止WHERE 1这种无效条件)
rewrite_rules 的复杂点在于 pattern 匹配不可视、调试困难;最容易被忽略的是 pattern_database 字段和 flush 动作——规则写了不 flush,等于没写。










