数据库中的用户与权限
如何在 MySQL 和 PostgreSQL 中为每个应用创建单独的用户、授予最小权限,并确认没有多余权限被允许。
更新于 2026年8月23日
以 root 或 postgres 身份运行的应用程序,一旦出现第一个漏洞,就会把机器上的所有数据库拱手交给攻击者。为项目创建专用用户,只授予其自身 schema 的权限,可以将损失限制在一个项目之内。
MySQL 和 MariaDB
这里的用户是“名称 + 主机”的组合,这也是最容易混淆的地方:
CREATE USER 'shop'@'localhost' IDENTIFIED BY 'long-random-password';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop'@'localhost';
FLUSH PRIVILEGES;
'shop'@'localhost' 和 'shop'@'%' 是两个不同的用户,密码也各不相同。除非确有必要,不要创建主机为 % 的记录:它允许从任意地址登录。
只有在执行迁移时才需要结构层面的权限:
GRANT CREATE, ALTER, DROP, INDEX, REFERENCES ON shop.* TO 'shop_migrate'@'localhost';
将应用角色与迁移角色分开是很好的做法:这样普通查询在物理上就不可能删除表。
检查:
SELECT user, host FROM mysql.user;
SHOW GRANTS FOR 'shop'@'localhost';
修改密码并删除:
ALTER USER 'shop'@'localhost' IDENTIFIED BY 'new-password';
DROP USER 'shop'@'localhost';
PostgreSQL
角色及其对数据库和 schema 的权限:
CREATE ROLE shop WITH LOGIN PASSWORD 'long-random-password';
CREATE DATABASE shop OWNER shop;
REVOKE ALL ON DATABASE shop FROM PUBLIC;
对于只读角色(报表、分析、监控面板):
CREATE ROLE reader WITH LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE shop TO reader;
GRANT USAGE ON SCHEMA public TO reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO reader;
最后一行是必须的:没有它,角色将看不到授予权限之后创建的表,一个月后报表就会因为新数据而出错。
检查:
\du
\l
\dp
必须锁定的内容
- 任何应用角色都不是超级用户。
- 每个项目都有自己的数据库和自己的用户。
- 密码要足够长且随机,不同项目使用不同的密码。
- 只对需要外部登录的地址开放外部登录——远程访问。
- MySQL 中不能有匿名用户,也不能有测试数据库:
mariadb-secure-installation会完成这项工作。
常见错误
| 现象 | 原因 |
|---|---|
密码正确却提示 Access denied | 匹配到了另一条主机规则,例如 ''@'localhost' |
已授予权限,但 SELECT 无法执行 | PostgreSQL 缺少 USAGE ON SCHEMA |
| 角色看不到新建的表 | 未设置 DEFAULT PRIVILEGES |
| 迁移失败时应用程序删除了所有数据 | 应用角色拥有 DROP 权限 |
| 密码出现在命令历史中 | 请从文件设置密码,并清除 ~/.mysql_history |
请将数据库密码存放在权限为
600的服务环境变量文件中,而不是写在代码里。一旦泄露,您只需修改一行并重启,而不必重建整个项目。