int强制转型不能防止sql注入,仅能拦截最基础的字符串拼接攻击;必须结合参数校验与preparedstatement绑定参数才能真正防护。

仅对 LIMIT 和 OFFSET 参数做 int 强制转型,不能防住 SQL 注入。 它最多拦住最基础的字符串拼接攻击,但绕过手段多、底层驱动行为不一致、配合其他逻辑(如动态排序)就彻底失效。
为什么 int 转型不是安全终点
很多人把 request.getParameter("size") 丢进 Integer.parseInt() 就以为高枕无忧,其实风险仍在:
- 转型失败后 fallback 到默认值(比如
0或1),而代码没校验范围,可能触发LIMIT 0, 1000000这类资源耗尽式查询 - 参数若被用于拼接
ORDER BY ${sortField},哪怕sortField是字符串,也完全不受int转型保护 - 某些 ORM(如老版本 MyBatis 使用
${})或自定义 SQL 拼接中,数字照样被当字符串插进 SQL,预编译根本没生效 - MySQL 5.7+ 才真正支持
LIMIT ? OFFSET ?的参数化;旧驱动或未开启useServerPrepStmts=true时,?仍会被客户端拼成字符串
必须用 PreparedStatement 绑定参数
核心是让数据库驱动把数值当作“数据”而非“SQL 片段”处理。以下才是可靠写法:
String sql = "SELECT * FROM orders WHERE status = ? ORDER BY created_at DESC LIMIT ? OFFSET ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, status); // 搜索条件 ps.setInt(2, size); // 必须先校验:> 0 && = 0 && <p>关键点:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/ai/3795" title="百小医"><img src="https://img.php.cn/upload/ai_manual/001/246/273/178599573970469.png" alt="百小医" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/ai/3795" title="百小医" class="overflowclass">百小医</a> <p class="overflowclass">百小医是一款AI工具,百川智能推出的AI家庭医生平台。</p> </div> <a rel="nofollow" href="/ai/3795" title="百小医" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
-
size和offset必须在setInt()前完成范围校验,不能只依赖转型 - MyBatis 用户请确认:所有分页参数都用
#{},禁用${};MyBatis-Plus 的Page对象本身安全,但手写自定义 SQL 时仍要检查是否真走预编译 - PostgreSQL 用
$1、SQL Server 用@size,原理相同——参数不进 SQL 字符串模板
ORDER BY 字段等无法参数化的部分只能白名单校验
LIMIT 和 OFFSET 可参数化,但 ORDER BY 后的字段名不行。例如 ORDER BY #{sortField} 在 MyBatis 中会报错,因为字段名不能当参数传。
此时唯一安全做法是白名单硬控制:
- 只允许
"id"、"created_at"、"status"等明确列出的字段 - 拒绝任何不在白名单中的输入,直接返回 400,不尝试“修复”或 fallback
- 避免用反射、
Enum.valueOf()等间接方式映射字段名,防止绕过
真正难的不是写对 setInt(1, size),而是整条查询链路上每个用户可控点(分页、排序、搜索、过滤)都得过同一套校验和传参规则。漏掉一个 ${} 或一个没校验的 sortField,前面所有转型和预编译就归零。










