数据库账号必须按功能切分,不能共用一个账号;应建web_read、web_write、report_user三类账号,禁用file/execute/process等高危权限,开启mysql严格模式,并杜绝密码硬编码。

数据库账号必须按功能切分,不能共用一个账号
共用账号是SQL注入危害放大的主因。哪怕只给SELECT权限,如果六个模块都用同一个账号,攻击者一旦从登录接口注入,就能扫出所有表名、字段名,甚至跨表关联查询。真实事故里,报表SQL拖垮主库,根本原因是运维、业务、导出全用同一账号,连连接池都混在一起。
按场景建三类账号是最小可行方案:
-
web_read:仅授予SELECT权限,限定到具体库+具体表(如GRANT SELECT ON myapp.users TO 'web_read'@'%') -
web_write:只给INSERT, UPDATE, DELETE,且必须限制 WHERE 条件字段(例如禁止无WHERE user_id = ?的全表更新) -
report_user:用角色封装多张表的SELECT,再把角色授给账号,避免直接 GRANT 多表
禁止 FILE、EXECUTE、PROCESS 等高危权限,哪怕测试环境也不行
这些权限在 MySQL 中属于“越权执行”类能力,和业务逻辑完全无关。比如 FILE 允许读写服务器任意文件(SELECT ... INTO OUTFILE 可导出 /etc/passwd),PROCESS 能看到其他账号正在执行的 SQL——这两项在绝大多数 PHP 应用里毫无存在必要。
检查方式很简单:
- 执行
SELECT * FROM mysql.user WHERE User = 'your_app_user';,确认File_priv、Process_priv、Super_priv全为N - 用
SHOW GRANTS FOR 'your_app_user'@'%';看实际生效权限,别只信建账号时写的语句
MySQL 严格模式必须开启,否则权限再小也白搭
关掉 STRICT_TRANS_TABLES 或 ONLY_FULL_GROUP_BY,会让攻击者绕过字段数校验。比如正常 UNION SELECT 需要匹配列数,但关闭严格模式后,SELECT 1,2,3 FROM dual 就能强行拼接系统表,读出 mysql.user 密码哈希。
验证是否生效:
- 执行
SELECT @@sql_mode;,输出中必须包含STRICT_TRANS_TABLES和ONLY_FULL_GROUP_BY - PHP 连接时显式设置:在 PDO DSN 后加
&mysql_mode=STRICT_TRANS_TABLES,或在mysqli::real_connect()前调用mysqli_options($link, MYSQLI_OPT_INIT_COMMAND, "SET sql_mode='STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY'")
密码不能硬编码,密钥管理必须脱离代码库
权限配得再细,只要数据库密码明文写在 .env 或 config/database.php 里,服务器一旦被拿 shell,攻击者 cat 一下就全连上了。这不是理论风险,是 90% 被拖库项目的共同起点。
生产环境必须做到:
- PHP 进程启动前,由容器或 systemd 注入环境变量(如
DB_PASSWORD),代码里只读$_ENV['DB_PASSWORD'] - 云环境优先用 Secret Manager(AWS Secrets Manager / Aliyun KMS),本地部署可用 HashiCorp Vault,禁止任何配置文件落盘存密码
- CI/CD 流水线中禁用
.env提交,Git 忽略规则加config/database.php和.env
权限最小化不是设完就完的事。每次加新接口,都要问一句:这个 SQL 真的需要查这张表?能不能用视图限制字段?账号是不是复用了旧的?
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











