不能。只读用户无法防御sql注入,因select类注入仍可拖库或盲注;必须配合参数化查询实现变量与sql的物理隔离,否则拼接字符串仍存在风险。

只读用户真能防SQL注入吗
不能。只读用户只限制写操作,对 SELECT 类注入完全无效——攻击者照样能用 UNION SELECT、AND 1=2 OR SLEEP(5) 等方式拖库、盲注、探测结构。
为什么必须配合参数化查询
只读权限只是纵深防御的一层,真正拦住注入的是变量与SQL语句的物理隔离。没参数化,拼接字符串就是把钥匙塞进SQL里:
-
WHERE name = '" + userInput + "'→ 攻击者输' OR '1'='1就绕过条件 -
WHERE id = ?(预编译)→ 数据被当纯值处理,?处永远无法变成语法的一部分 - ORM如Django的
.filter(name=userInput)、SQLAlchemy的session.query().filter(User.name == userInput)默认走参数化,但显式用text()或execute("SELECT ... " + userInput)就立刻失效
只读用户该怎么做才不白设
最小权限原则不是设完就完,得卡死入口和行为边界:
- 数据库创建用户时明确指定
GRANT SELECT ON db.table TO 'report_user'@'%',别用GRANT SELECT ON *.* - 应用连接串里固定用这个只读账号,严禁在代码里动态切换账号或拼接
SET SESSION sql_mode=...等提升权限的语句 - 禁止给只读用户
SHOW DATABASES、INFORMATION_SCHEMA查询权限(除非业务强依赖),否则攻击者能枚举表名字段名 - 监控日志里出现
SELECT ... FROM mysql.user、SELECT ... FROM information_schema.tables这类语句,基本就是注入探测信号
常见误判:加了单引号/转义就安全
魔改字符串不如不用——MySQL的 mysql_real_escape_string(已弃用)、PHP的 addslashes()、JS的简单 replace(/'/g, "\'") 全部失效于多字节编码、宽字节注入、非ASCII上下文等场景。
- 攻击载荷如
%df%27 OR 1=1 --(GBK宽字节)可绕过addslashes - JSON字段里嵌SQL、日志回显点拼接SQL、存储过程内动态拼接,都逃不开参数化
- 连
ORDER BY和LIMIT后的参数都不能直接插,得用白名单校验(如['id', 'created_at'])或绑定整数参数(部分驱动支持LIMIT ?, ?)
只读用户+参数化是两条腿走路,少一条都会摔。最常被漏掉的是:连只读账号本身,也得定期审计它到底查了哪些表、哪些字段——权限是活的,不是建完就封印了。










