数据库最小权限实践
以 MySQL 为例,讲清楚为什么别用 root 跑业务、怎么按库按表授权、只给必要权限、限定访问来源,配真实 SQL。
「最小权限」是安全里最朴素也最有用的一条原则:每个账号只给它完成本职工作所需的最小权限,多一点都不给。数据库尤其要讲究这个——一旦某个业务账号被人拿到,它能干的事越少,损失就越小。
这篇以 MySQL / MariaDB 为例,讲清楚怎么落地。命令换成 PostgreSQL 思路一样,只是语法不同。
一、为什么不能用 root 跑业务
很多人图省事,网站直接用 root 连数据库。这非常危险:
- root 能删库、改任何表、创建新用户、读所有数据。
- 一旦你的网站有 SQL 注入之类的漏洞,攻击者借着这个 root 连接,就能对整个数据库为所欲为——包括
DROP DATABASE(删库跑路真不是段子)。 - 反过来,如果业务账号只有某个库的读写权限,就算被利用,攻击者也碰不到其它库、删不了库、建不了新账号。
结论:业务程序永远用专用的、权限受限的账号连数据库,root 只留给管理员手动运维。
二、给每个应用建独立账号
假设你有一个博客应用,用一个叫 blog 的数据库。给它建一个专属账号:
-- 只允许从本机连接(@'localhost'),密码用强密码
CREATE USER 'blog_app'@'localhost' IDENTIFIED BY '换成一个足够强的密码';这里的 @'localhost' 很关键,下面第四节会细说。
三、只授予必要的权限
一个典型的网站业务,通常只需要对自己那个库做增删改查,用不着建表删表、更用不着碰别的库。所以只给这几样:
-- 只对 blog 这个库的所有表,授予查询/插入/更新/删除四种权限
GRANT SELECT, INSERT, UPDATE, DELETE ON blog.* TO 'blog_app'@'localhost';
-- 让权限生效
FLUSH PRIVILEGES;对比一下,下面这些是绝大多数业务账号都不该有的危险权限:
| 权限 | 能干什么 | 为什么别给业务账号 |
|---|---|---|
ALL PRIVILEGES | 所有权限 | 等于半个 root,范围太大 |
DROP | 删表 / 删库 | 被注入后可直接删库 |
GRANT OPTION | 给别人授权 | 能自己给自己提权 |
CREATE USER | 建新账号 | 攻击者可留后门账号 |
FILE | 读写服务器文件 | 可能被用来读系统文件 |
如果某个后台管理功能确实需要建表(比如安装程序),可以临时授予、用完立刻收回,别长期挂着。
权限还能精确到表和列
如果想更细,权限可以只给某几张表,甚至某几列:
-- 只给 blog 库里 posts 表的查询和更新权限
GRANT SELECT, UPDATE ON blog.posts TO 'some_user'@'localhost';
-- 只允许更新 posts 表的 title 和 content 两列
GRANT UPDATE (title, content) ON blog.posts TO 'some_user'@'localhost';一个常见场景:给「只读报表 / 数据分析」的账号,就只给 SELECT,它连改数据的能力都没有,最安全。
CREATE USER 'report_readonly'@'192.168.1.50' IDENTIFIED BY '强密码';
GRANT SELECT ON blog.* TO 'report_readonly'@'192.168.1.50';
FLUSH PRIVILEGES;四、限定访问来源(谁能连)
MySQL 账号的完整身份是 '用户名'@'来源',那个 @ 后面决定了这个账号只能从哪里连进来。用好它,等于给账号加了一道网络锁:
'blog_app'@'localhost'—— 只能从数据库本机连。网站和数据库在同一台服务器时,用这个最安全。'blog_app'@'192.168.1.50'—— 只能从这个内网 IP 连。网站和数据库分开部署时用。'blog_app'@'%'—— 任何 IP 都能连,尽量别用。这等于把数据库门开向全世界。
千万别为了图方便用 @'%' 再把 3306 端口对公网开放,这是数据库被拖库的头号原因。数据库端口应该只在本机或内网可达,配合 服务器安全加固清单 里的防火墙一起做。
五、检查与回收权限
查看某个账号现在有哪些权限:
SHOW GRANTS FOR 'blog_app'@'localhost';发现权限给多了,收回来:
-- 收回 blog 库上的 DELETE 权限
REVOKE DELETE ON blog.* FROM 'blog_app'@'localhost';
FLUSH PRIVILEGES;删掉不再使用的账号(离职的人、废弃的应用,及时清掉):
DROP USER 'old_user'@'localhost';六、几条配套习惯
- 强密码 + 不复用:每个数据库账号用独立的强密码,别和别处共用。
- 密码别写进代码仓库:放到环境变量或配置文件里,并确保配置文件不被提交到 Git。
- 定期审计账号:隔段时间用
SELECT user, host FROM mysql.user;看看有哪些账号,清掉不认识的、不用的。 - 关掉匿名账号和测试库:新装的 MySQL 建议跑一遍
mysql_secure_installation,它会帮你删匿名用户、删 test 库、禁止 root 远程登录。
一页速查清单
- 业务程序绝不用 root 连库
- 每个应用一个独立账号
- 只授予实际需要的权限(多数只要 SELECT/INSERT/UPDATE/DELETE)
- 只读场景就只给 SELECT
- 不给 DROP / GRANT OPTION / CREATE USER / FILE 等高危权限
- 用
@'localhost'或指定 IP 限定来源,别用@'%' - 数据库端口不对公网开放
- 定期
SHOW GRANTS审计、清理废弃账号 - 跑
mysql_secure_installation做基础收敛