必须用语义化域名(如mysql-prod.example.com)替代IP硬编码,通过DNS隔离;禁止代码中硬写连接参数,应使用环境变量或配置文件;生产库账号仅授SELECT等最小权限;客户端启用safe-updates模式;自动化部署前用SQL解析器拦截高危语句。

连接串里必须带环境标识且禁止硬编码
开发人员直接用生产库账号连错库,八成是因为连接配置没做隔离。最简单的防线是让连接串本身暴露环境信息,比如把 host 写成 mysql-prod.example.com 而不是 10.10.10.10,再配合终端提示符或 IDE 标签页颜色区分。
硬编码在代码里(如 Python 的 mysql.connector.connect(host='127.0.0.1', user='root') )是高危行为,必须用配置文件或环境变量加载。推荐方式:
-
DB_HOST设为mysql-dev/mysql-staging/mysql-prod这类语义化域名,DNS 层做解析隔离 - 所有项目根目录放
.env,但.gitignore必须包含它,防止误提交 - CI/CD 流水线中禁止传入
prod类关键词的变量名,Jenkins 或 GitHub Actions 可加前置检查脚本
MySQL 用户权限按环境严格分层
一个账号通吃 dev/staging/prod 是最大隐患。生产库账号应默认只有 SELECT 和极少数白名单 UPDATE 权限,且禁止 DROP、TRUNCATE、ALTER TABLE。
实操建议:
- 建用户时用
CREATE USER 'app-prod-ro'@'%' IDENTIFIED BY 'xxx';,再GRANT SELECT ON prod_db.* TO 'app-prod-ro'@'%'; - 开发账号可给
CREATE TEMPORARY TABLES和EXECUTE,但禁用FILE、PROCESS、SUPER - 用
SHOW GRANTS FOR CURRENT_USER;在连接后立刻验证权限,CI 脚本里可加这行断言
SQL 客户端强制启用 safe-updates 模式
MySQL 命令行客户端和部分 GUI 工具(如 DBeaver)支持 --safe-updates(或 sql_safe_updates=1),开启后所有 UPDATE 和 DELETE 必须带 WHERE 且不能是全表扫描——这对防手抖删库很有效。
注意点:
- 该模式只影响客户端行为,服务端不感知,所以必须在每个开发机的
~/.my.cnf里配:[client] safe-updates
- 某些 ORM(如 Django 的
manage.py dbshell)会绕过这个设置,得额外在 shell 启动时加--safe-updates - 生产环境数据库的 MySQL 配置文件(
my.cnf)里不要设sql_safe_updates=1,否则运维脚本可能失败
自动化流程中用 SQL 解析器拦截高危语句
光靠人盯和权限不够,尤其当 DBA 不参与每次上线时。可在部署前加一道 SQL 静态检查,比如用 pt-query-digest 或自研脚本识别 DROP DATABASE、TRUNCATE TABLE、无 WHERE 的 DELETE。
轻量级方案示例(Bash):
grep -E '^(DROP|TRUNCATE|DELETE FROM [^ ]+;)$|DELETE FROM [^ ]+ WHERE' deploy.sql && echo "ERROR: unsafe SQL detected" && exit 1
更稳妥的做法是接入 sqlfluff 或 mysqllint,它们能识别语法结构而非简单关键词匹配,避免误报 DELETE FROM logs WHERE created_at < NOW() - INTERVAL 30 DAY 这类合法语句。
真正难防的是带变量的动态 SQL,比如 CONCAT('DELETE FROM ', table_name) —— 这种只能靠代码审计和运行时日志监控,没法靠静态检查兜底。


















