最小权限不能阻止SQL注入发生,但能使其多数语句执行失败或返回空数据;只要数据库账号未被授予对应表、字段或操作权限,UNION SELECT等语句将直接报错ERROR 1142。

最小权限不能阻止SQL注入发生,但能让绝大多数注入语句执行失败或只返回空数据——只要账号没被授予对应表、字段或操作的权限,UNION SELECT、AND 1=(SELECT ...) 这类语句会直接报 ERROR 1142 (42501): SELECT command denied。
MySQL 中如何精确限制到单表单列
很多人以为 GRANT SELECT ON myapp.users TO 'app'@'%' 就够了,其实这等于开放整张表所有字段。攻击者仍可用 UNION SELECT credit_card, cvv FROM payments 横跳取数,前提是账号也有 payments 表权限。
- 必须显式列出字段:
GRANT SELECT (id, username, email) ON myapp.users TO 'web_ro'@'192.168.10.%' - 立刻撤销宽泛授权:
REVOKE SELECT ON *.* FROM 'web_ro'@'192.168.10.%'(否则可能隐式继承) - 验证是否生效:
SHOW GRANTS FOR 'web_ro'@'192.168.10.%',输出里不能出现ON *.*或ON myapp.* - 别用
GRANT SELECT ON myapp.*—— 新增表默认就可查,相当于埋雷
PostgreSQL 默认 public schema 是个坑
即使你没给账号授任何权限,只要没显式收回 public schema 的访问权,攻击者就能扫库:SELECT table_name FROM information_schema.tables 直接跑出所有表名。
- 必须显式禁用:
REVOKE USAGE ON SCHEMA public FROM PUBLIC; - 再单独授业务视图或表:
GRANT SELECT ON TABLE orders TO app_ro; - 检查视图权限链:
\z public.active_users,确认 ACL 里没有postgres=arwdDxt/postgres这类宽泛条目 - 如果视图里调用了
SECURITY DEFINER函数,且该函数拥有pg_read_all_data角色,整个视图就失效
SQL Server 里 db_datareader 不等于安全
db_datareader 看似只读,但它默认允许查 master、msdb 等系统库——攻击者能直接 SELECT name FROM master..sysdatabases 扫库,甚至查登录账号哈希。
- 必须显式禁止:
DENY SELECT ON SCHEMA::sys TO app_user; - 用角色隔离更可控:
CREATE ROLE app_reader; GRANT SELECT ON OBJECT::users TO app_reader; - 禁用
xp_cmdshell没用,它不影响UNION SELECT @@version这类基础探测 - 真正关键的是:
DENY CONTROL SERVER TO app_user;和ALTER ROLE db_datareader ADD MEMBER app_user;要配对使用
权限配置后最容易被绕过的三个盲点
很多团队配完权限就以为万事大吉,结果上线新功能时悄悄加了 JOIN 多张表,或者开了 LOCAL INFILE,导致防护形同虚设。
-
sql_mode被关掉:MySQL 5.7+ 默认启用STRICT_TRANS_TABLES,但旧项目常手动关掉。一旦关闭,SELECT 1,2,3 FROM dual可绕过字段数校验,强行UNION出系统表 -
secure_file_priv为空:攻击者可用SELECT ... INTO OUTFILE把敏感数据写到 Web 目录下,再通过 HTTP 下载 - 密码硬编码在配置文件:哪怕权限再小,服务器被拿 shell 后,
cat config.php就能连库——必须用密钥管理服务或运行时注入凭证
最小权限不是上线前配一次就结束的事。每次新增 SQL 查询语句、新建表、改存储过程,都得重跑一遍 SHOW GRANTS 和 SELECT @@sql_mode,否则新代码就是新缺口。


















