归档脚本只需 select 和 delete 权限,严禁 insert/update;权限须精确到目标表,禁止模糊授权;delete 无 where 限制,需靠硬编码安全条件和 sql 审计兜底。

归档脚本只需要 SELECT + DELETE,别碰 INSERT/UPDATE
数据归档脚本的核心逻辑是「查旧数据 → 删旧数据」,典型场景如按时间清理 logs 表中 90 天前的记录。它不写新数据、不改状态、不关联其他表,所以只需 SELECT 和 DELETE 权限,且必须限定在目标表范围内。
常见错误是顺手加 INSERT(以为“可能要搬数据到归档库”),但归档脚本本身不负责写入——那是另一个独立服务或 ETL 工具的事。混用权限会扩大攻击面:一旦脚本被注入或配置泄露,攻击者能伪造插入恶意日志,甚至通过 INSERT ... SELECT 回写敏感字段。
- 正确做法:
GRANT SELECT, DELETE ON app_db.logs TO 'archiver'@'10.20.5.%' - 禁止模糊授权:
app_db.*或*.*都不行,新增表自动继承权限等于埋雷 - 如果归档逻辑含子查询关联其他表(比如只删用户已注销的订单),需额外授予
SELECT权限给那张关联表,但依然不加DELETE
DELETE 权限无法限制 WHERE 条件,靠 SQL 审计兜底
MySQL 的 DELETE 权限是表级的,一旦授予,用户就能执行任意 DELETE FROM table WHERE ...,包括 DELETE FROM table(全表删)。它不识别 WHERE 是否带时间条件、是否加了 JOIN 过滤——这是应用层逻辑,数据库权限系统不管。
这意味着:权限本身防不了误删,只能靠流程和工具补位。
- 归档脚本必须硬编码安全 WHERE 条件,例如:
WHERE created_at ,禁止拼接用户输入 - 上线前强制走 SQL 审计平台(如 SOAR 或自建解析器),拦截无 WHERE 或 WHERE 恒真的
DELETE - 生产环境禁用
sql_safe_updates=0,确保所有DELETE必须带 KEY 条件或 LIMIT(虽不能替代权限,但多一层保险)
避免用 % 主机名,IP 段白名单才是最小化网络暴露
归档脚本通常跑在固定运维节点或 Kubernetes Job Pod 中,来源 IP 可控。用 'archiver'@'%' 等于把 DELETE 权限暴露给整个网络,任何能连上 MySQL 的机器都可冒用该账号。
真实部署中,IP 段比主机名更可靠:K8s Pod IP 动态变化,但所在 Node 或 Service CIDR 是固定的;物理机运维节点也常有稳定内网段。
- 推荐写法:
GRANT SELECT, DELETE ON app_db.logs TO 'archiver'@'10.20.5.%'(对应运维网段) - 若脚本跑在容器里且 IP 不固定,改用 DNS 解析+
max_connections限流,而非放行% - 绝对禁止
'archiver'@'localhost'用于远程调度——本地账号无法从调度中心连接,容易倒逼出临时开%的妥协方案
权限变更后必须 FLUSH PRIVILEGES,否则 DELETE 仍可能生效
MySQL 权限缓存在内存中。GRANT 或 REVOKE 只改磁盘上的 mysql.db 表,不自动重载。你执行完授权语句,立刻用该账号连上去测试 DELETE,很可能发现「明明刚授了权,却报 Access denied」,或者更危险的情况:「刚 revoke 了 DELETE,还能删」。
这不是权限没生效,是缓存没刷。
- 每次授权/回收后,必须紧跟:
FLUSH PRIVILEGES - 自动化脚本里建议封装成原子操作,例如 Bash 函数:
mysql -e "GRANT SELECT, DELETE ON app_db.logs TO 'archiver'@'10.20.5.%'; FLUSH PRIVILEGES;" - 注意:
FLUSH PRIVILEGES是全局锁操作,高并发时段慎用;归档脚本权限一般只在部署期变更,避开业务高峰即可
SELECT 那样能拦住敏感列,也不像角色那样能动态开关。一旦给了,就默认信任脚本逻辑的健壮性。所以真正的最小化,不单在权限语句里少写几个单词,而在于把 WHERE 条件、执行环境、审计机制全链路卡死。











