mysql中权限分离与最小权限原则的应用

来源:这里教程网 时间:2026-02-28 20:42:36 作者:

MySQL 中如何为应用账号分配最小必要权限

直接给

SELECT
INSERT
UPDATE
DELETE
就够了,别碰
DROP
ALTER
CREATE
,更不能给
GRANT OPTION
FILE
权限。生产环境的应用账号,绝大多数场景下只需对特定数据库的几张表有 CRUD 权限。

常见错误是用

GRANT ALL PRIVILEGES ON *.* TO 'app'@'%'
—— 这等于把整套 MySQL 当成裸机交出去,一旦应用被注入或配置泄露,数据库就全盘失守。

先创建专用数据库:
CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
再建账号并限制来源:
CREATE USER 'app_user'@'10.20.%.%' IDENTIFIED BY 'strong_password_123';
只授指定库表权限:
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.orders TO 'app_user'@'10.20.%.%';
执行
FLUSH PRIVILEGES;
生效(仅在修改系统表后需显式调用)

为什么不能用 root 或同级账号跑应用服务

root 账号拥有

SUPER
SHUTDOWN
RELOAD
等高危权限,还能读写
mysql
系统库。应用代码若存在 SQL 注入、日志打印敏感 SQL、或连接串误泄露,攻击者可直接执行
SELECT * FROM mysql.user;
获取所有账号哈希,甚至用
SET PASSWORD FOR 'root'@'localhost' = ...
提权。

更隐蔽的风险是 ORM 自动生成的语句可能触发隐式权限需求。比如 Django 的

syncdb
或 Laravel 的
migrate
默认尝试
SHOW CREATE TABLE
SELECT
系统表 —— 这些只需
SELECT
权限即可满足,无需
PROCESS
INFORMATION_SCHEMA
全局访问。

应用账号不应出现在
mysql.user
表的
Super_priv='Y'
行中
避免使用
'%'
作为 host,优先限定内网子网段(如
'10.20.30.%'
禁止授予
GRANT OPTION
—— 否则该账号可自行扩大权限范围

如何安全地支持 DBA 日常运维与开发查数需求

运维和开发不是“非得用 root 才能干活”。应分角色建三类账号:应用账号(最小权限)、只读分析账号(

SELECT
+
SHOW VIEW
)、DBA 账号(有限
ALTER
/
INDEX
权限,禁用
DROP DATABASE
)。

例如导出报表的 BI 工具,只需:

GRANT SELECT ON app_db.* TO 'bi_reader'@'192.168.100.%';
;而 DBA 账号可授权:
GRANT SELECT, INSERT, UPDATE, DELETE, INDEX, ALTER ON app_db.* TO 'dba_team'@'10.10.%.%';
—— 注意没给
DROP
,删表必须走工单+二次确认流程。

SHOW DATABASES
权限默认对所有用户隐藏,如需显示,单独授予(但不推荐)
临时查数用的账号,建议配
MAX_QUERIES_PER_HOUR 100
MAX_UPDATES_PER_HOUR 0
防误操作
避免在应用配置中硬编码账号密码;改用 MySQL 8.0+ 的
CACHING_SHA2_PASSWORD
插件 + 密码轮换策略

MySQL 8.0 权限模型变化带来的实操调整

MySQL 8.0 引入角色(

ROLE
)和动态权限(如
BACKUP_ADMIN
),但角色不能替代最小权限原则——它只是批量授权的语法糖。真正关键的变化是:默认启用
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
,且
mysql.user
表结构彻底重构,旧版通过直接 UPDATE 系统表改权限的方式已失效。

这意味着你不能再靠脚本去“偷偷”更新

mysql.user
,所有权限变更必须走
GRANT
/
REVOKE
语句,而这些语句本身会被审计日志记录(如果启用了
audit_log
插件)。

创建角色示例:
CREATE ROLE 'app_rw'; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_rw';
绑定用户:
GRANT 'app_rw' TO 'app_user'@'10.20.%.%';
,之后需执行
SET DEFAULT ROLE 'app_rw' TO 'app_user'@'10.20.%.%';
动态权限如
CONNECTION_ADMIN
仅用于管理连接,不应授予应用账号
SELECT user, host, account_locked, password_last_changed 
FROM mysql.user 
WHERE user IN ('app_user', 'bi_reader');

权限分离不是加几行 GRANT 就完事,真正的难点在于持续识别“哪些权限现在看起来多余,但某天某个新功能又悄悄依赖它”。每次上线新模块前,务必重审该模块实际执行的 SQL 类型,并反向收缩账号权限。

相关推荐