最小权限比预处理更关键,因预处理仅防拼接注入,而高权限账号可利用合法操作(如insert into mysql.user)或select/information_schema读取、file读写、grant option提权等绕过防护。

防范因权限过度开放导致的注入风险,核心不是“加固SQL写法”,而是让攻击者即使拿到注入点,也因账号权限太低而无法利用——SELECT 权限都可能被滥用,INSERT 或 UPDATE 权限配合系统表写入则直接失控。
为什么最小权限比预处理更关键
预处理语句能防拼接型注入,但挡不住已授权账号的合法操作。只要应用账号有 mysql.user 的 INSERT 权限,攻击者就能执行 INSERT INTO mysql.user ... 添加后门账号;哪怕你代码 100% 用 PDO::prepare(),照样生效。
- 报错注入、布尔盲注、延时注入不依赖拼接,只依赖账号有
SELECT权限读取information_schema -
FILE权限开启后,LOAD_FILE()和SELECT ... INTO OUTFILE可直接读写服务器文件,和 SQL 注入是否参数化无关 -
GRANT OPTION被授予后,攻击者可给自己加权,甚至创建新高权限账号 - 权限检查发生在 SQL 解析前,数据库根本不关心你是用 ORM 还是 raw query,只看执行者身份
必须立刻检查并回收的三类高危权限
运行 SHOW GRANTS FOR 'app_user'@'%';,重点排查以下权限是否存在。只要出现任意一项,立即 REVOKE:
-
FILE:允许读写服务器任意路径(LOAD_FILE(),INTO OUTFILE),应设secure_file_priv为非空目录,并禁用该权限 -
GRANT OPTION:允许账号把已有权限再授给他人,极易造成权限扩散 -
mysql.*上的SELECT、INSERT、UPDATE、DELETE:这是系统表注入的温床,REVOKE ALL PRIVILEGES ON mysql.* FROM 'app_user'@'%';是底线操作
列级权限 + 存储过程才是真实可用的收敛手段
单纯限制到表级仍不够。比如登录模块只需查 users.username 和 users.password_hash,其他字段如 is_admin、api_token 必须隔离。
- 用列级授权代替
SELECT *:GRANT SELECT (username, password_hash) ON myapp.users TO 'web_login'@'10.20.30.%'; - 敏感操作封装进
DEFINER存储过程:CREATE DEFINER = 'dba_secure'@'localhost' PROCEDURE sp_auth_login(...),调用者只需EXECUTE权限,无需任何表访问权 - 避免在应用层动态拼接表名或字段名——这类场景无法参数化,只能靠权限+视图/存储过程兜底
最容易被忽略的部署盲区
很多团队花力气审计 PHP 或 Java 代码里的 SQL 拼接,却从不检查上线时创建的数据库账号权限。开发环境用 root 测试,上线脚本里没改账号,或者自动化部署工具硬编码了 GRANT ALL,结果线上账号永远带着 SUPER 和 mysql.* 权限跑。
每次部署后必须人工或脚本验证:SELECT user, host, authentication_string FROM mysql.user WHERE user = 'app_user'; + SHOW GRANTS,否则所有上层防护都是摆设。











