应使用最小权限连接池账号(仅usage和健康检查select),业务操作通过definer存储过程执行,禁止sql拼接与动态标识符传入,实现连接、执行、校验三域解耦。

为什么不能用应用账号直连数据库
应用代码里写死数据库账号密码,等于把权限控制权交给了开发和运维流程——只要代码能访问,就无法阻止它执行 DROP TABLE 或读取敏感字段。更麻烦的是,一旦某服务被攻破或出现逻辑漏洞,攻击者直接继承应用账号的全部权限。
真正解耦的关键不是“换一个账号”,而是让连接池账号只具备连接能力,不带业务语义权限;所有数据操作权限收归存储过程、视图或代理层管控。
- 连接池账号只需
USAGE权限 +SELECT权限(仅用于健康检查) - 业务操作必须通过预定义的
DEFINER存储过程,由数据库账号代为执行 - 禁止在应用中拼接 SQL,尤其不能传入表名、字段名、
ORDER BY字段等动态标识符
如何配置最小权限连接池账号
MySQL 8.0+ 推荐用角色(role)管理权限,避免给账号硬编码一堆 GRANT。连接池账号本身不授任何表级权限,只赋予连接能力和基础会话控制权。
示例建号语句:
CREATE USER 'pool_user'@'%' IDENTIFIED BY 'strong_pwd'; GRANT USAGE ON *.* TO 'pool_user'@'%'; GRANT SELECT ON mysql.health_check TO 'pool_user'@'%'; -- 假设你有个轻量健康检查视图 FLUSH PRIVILEGES;
-
USAGE是空权限占位符,允许登录但什么都干不了 - 别给
SUPER、PROCESS、REPLICATION CLIENT等高危权限 - 如果用 ProxySQL 或 Vitess,连接池账号甚至可以限制只允许从代理 IP 连入:
'pool_user'@'10.20.30.%'
业务权限怎么落到具体操作上
靠存储过程 + DEFINER 实现“以谁的身份执行”。定义者账号(比如 app_logic)拥有业务所需的全部权限,而调用者(pool_user)只需要 EXECUTE 权限。
例如一个安全的用户查询接口:
CREATE DEFINER = 'app_logic'@'localhost' PROCEDURE get_user_by_id(IN uid INT) READS SQL DATA SQL SECURITY DEFINER BEGIN SELECT id, nickname, created_at FROM users WHERE id = uid AND status = 'active'; END
- 调用时只需
CALL get_user_by_id(123),无需暴露表结构或 WHERE 条件细节 -
SQL SECURITY DEFINER确保权限检查按app_logic账号来,不是调用者 - 所有输入参数自动转义,天然防注入;输出字段也由过程固定,不会意外泄露
password_hash
连接池配置里最容易漏掉的三项
很多团队配好了账号,却在连接池层面翻车:权限没失效,但行为失控。
- 关闭
autoReconnect=true(JDBC)——它可能绕过权限变更,复用旧连接 - 设置
sessionVariables=sql_mode='STRICT_TRANS_TABLES',防止宽泛模式掩盖越权写入 - 显式指定
useSSL=true&requireSSL=true,否则中间人可劫持连接并冒用pool_user身份重放请求
权限解耦不是加一层账号就完事,是把连接、执行、校验三个环节拆到不同信任域里。最常被忽略的是:存储过程里的权限检查没法覆盖动态列名或分区名,这类场景必须回到应用层做白名单校验,数据库只负责兜底。











