最小权限原则无法阻止sql注入,但能限制攻击后果;mysql列级授权须显式指定字段,否则默认全列可查;严格模式、public schema权限、密码明文存储是三大易忽略的失效漏洞;存储过程+definer机制可安全实现动态查询。

SQL注入发生后,权限决定你能丢多少数据
最小权限原则不能阻止SQL注入发生,但能直接限制攻击者拿到数据库账号后能干成什么事。注入语句最终是以应用连接账号的身份执行的,如果这个账号只有SELECT (id, username)权限,那UNION SELECT @@version, user(), database()会报错,DROP TABLE logs会被拒绝——不是“防不住就全完”,而是“防不住也只丢一列”。
MySQL列级授权必须显式写字段,否则等于全开
很多人以为GRANT SELECT ON myapp.users TO 'web'只是给了表权限,其实这是全列授权。真实可控的最小权限必须写出字段列表:
-
GRANT SELECT (id, username, email) ON myapp.users TO 'web_ro'@'%'—— 安全 -
GRANT SELECT ON myapp.users TO 'web_ro'@'%'—— 危险,password_hash、phone、is_admin 全部可查 - 不写字段 = MySQL 默认允许查所有列,连
SHOW COLUMNS FROM users都能成功
验证方式:SHOW GRANTS FOR 'web_ro'@'%',确认输出里没有ON myapp.*或ON *.*。
三个常被忽略却立刻让最小权限失效的配置漏洞
权限配得再细,踩中以下任意一点,前功尽弃:
- MySQL 关闭了严格模式:
SELECT @@sql_mode返回结果里不含STRICT_TRANS_TABLES,攻击者可用SELECT 1,2 FROM dual绕过UNION字段数校验,强行拼出系统表 - PostgreSQL 的
publicschema 没收回默认读权限:REVOKE USAGE ON SCHEMA public FROM PUBLIC没执行,攻击者直接SELECT table_name FROM information_schema.tables扫库 - 账号密码明文写在
config.yml或.env里,服务器一旦被拿 shell,cat config.yml | grep password就能连库——权限再小也没用
存储过程 + DEFINER 是绕过权限粒度限制的务实方案
当业务需要动态条件但又不想开放整张表权限时,DEFINER机制比硬塞列级授权更可靠:
- 创建存储过程时指定
SQL SECURITY DEFINER,执行时以定义者(如'dba_secure'@'localhost')权限运行 - 调用者只需
EXECUTE权限,不需要对基表有任何SELECT授权 - 即使参数被注入成
"admin' OR '1'='1",也只是影响WHERE子句的字符串匹配,不会改变SQL结构
真正难落地的不是语法,而是每次加新接口、改JOIN逻辑、新增搜索字段时,都要重走一遍权限评审:这个SQL真的需要查mysql.user?











