limit后不能用参数化查询,因sql解析器要求其值为字面整数;需用正则^[0-9]+$校验输入、强转整型并检查范围(如1–1000),否则易遭注入攻击。

为什么 LIMIT 后不能用参数化查询
几乎所有主流数据库驱动(如 psycopg2、mysql2、sqlite3)都不支持把 LIMIT 或 OFFSET 的值作为绑定参数传入。比如这句会直接报错:
cursor.execute("SELECT * FROM users LIMIT %s", [user_input])
错误通常是 syntax error at or near "$1" 或类似提示——因为 SQL 解析器在语法分析阶段就要求 LIMIT 后必须是字面整数,不是占位符。这不是 bug,是标准行为。
LIMIT 注入的典型攻击手法
当后端拼接字符串构造查询时,比如:
"SELECT * FROM logs ORDER BY created_at DESC LIMIT " + request.query.limit
攻击者就能传入恶意值触发不同后果:
-
limit=10; DROP TABLE logs--:若数据库允许多语句且驱动未禁用,可能直接删表 -
limit=-1:某些 MySQL 版本会返回全表(绕过分页限制) -
limit=999999999999999999999:触发全表扫描、OOM 或慢查询告警 -
limit=10,10:若代码只校验第一个参数,第二个参数仍可注入
如何安全地强制转为整数并校验
关键不是“转整数”,而是“从原始字符串开始严格验证”。以下步骤缺一不可:
- 用正则
^[0-9]+$检查输入是否为纯数字字符串(拒绝-1、+10、10.5、10、1e3) - 用语言原生强转函数(如 Python
int()、Gostrconv.Atoi()、Node.jsNumber())转成整型 - 立即检查范围:
limit > 0 && limit (业务上限需按实际设) - 任一失败就返回
400 Bad Request,不 fallback、不默认值、不记录日志后继续执行
示例(Python):
limit_str = request.args.get("limit", "")
if not re.match(r"^[0-9]+$", limit_str):
abort(400)
limit = int(limit_str)
if not (1 <h3>LIMIT 有两个参数时更危险</h3><p><code>LIMIT offset, count</code> 中两个值都必须校验。常见错误是只校验 <code>count</code>,忽略 <code>offset</code>:</p>
-
offset为负数或极大值会导致跳过大量数据或越界读取 - 若用
request.args.get("offset", "0")默认值,攻击者可省略参数但控制count实现注入 - 必须对两个参数分别走完整校验流程,且都用
^[0-9]+$正则开头
最容易被忽略的一点:校验必须在拼接 SQL 前完成,且不能依赖数据库层的“自动截断”或“类型隐式转换”——MySQL 的 CAST('10abc' AS SIGNED) 会静默变成 10,这等于放行了非法输入。











