导出接口易被sql注入,因常直接拼接order、sort、filter等非纯值参数,而order by/group by字段无法预处理,须白名单校验;like搜索需转义通配符;大数据量时还需防堆叠注入和时间盲注。

导出接口为什么容易被SQL注入
导出接口常直接拼接用户传入的 order、sort、filter 等参数进 SQL,尤其是动态字段名、排序方向、模糊搜索关键词这类“非纯值”参数——它们无法用预处理语句绑定,一不留神就成了注入入口。比如 ORDER BY ${sort_field} ${sort_dir} 这种写法,攻击者传 sort_field=id, (SELECT 1 FROM users WHERE username='admin')=1 -- 就可能触发子查询。
ORDER BY 和 GROUP BY 字段不能用 #{} 或 ? 绑定
数据库不允许把列名、表名、关键字(如 ASC/DESC)作为预编译参数传入,JDBC/MySQLi/PDO 都会报错:SQLSTATE[HY093]: Invalid parameter number 或 mysqli_sql_exception: You have an error in your SQL syntax。所以必须在 PHP/Java 层做白名单校验,而不是试图“转义”或“加引号”。
- 只允许
sort_field取值为预定义字段列表:['id', 'name', 'created_at', 'status'] -
sort_dir必须严格限制为'ASC'或'DESC',用in_array($dir, ['ASC', 'DESC'], true)判断,别信strtoupper()+str_replace() - 如果支持多字段排序(如
sort=id,name),需拆分后逐个白名单校验,再用implode(', ', $safe_fields)拼接
LIKE 搜索和模糊过滤要手动转义通配符
导出接口常带关键词搜索,比如 WHERE name LIKE '%{keyword}%'。若直接把用户输入塞进 LIKE 模式,%、_、 会被当作通配符执行,而攻击者可构造 %' OR 1=1 -- 绕过条件。预处理语句只能保护值本身,不改变 LIKE 的语义解析逻辑。
- PHP 中用
addcslashes($keyword, '%_\')转义原始字符,再手动加前后%:"%".addcslashes($_GET['q'], '%_\')."%" - MySQL 里配合
ESCAPE '\'使用:WHERE name LIKE ? ESCAPE '\',然后绑定转义后的字符串 - MyBatis 中避免写
LIKE '%${keyword}%'(${} 是字符串拼接,极度危险),改用LIKE CONCAT('%', #{keyword}, '%'),并确保#{keyword}已经过addcslashes处理
导出数据量大时更要防堆叠注入和时间盲注
导出接口往往不设行数限制,且响应体大、耗时长,攻击者可能利用这点发起堆叠注入(如 MySQL 的 ; 分隔多语句)或时间盲注(SLEEP(5))。即使你用了预处理,若底层驱动或配置允许多语句执行,风险仍在。
- MySQLi 连接时禁用多语句:
$mysqli->options(MYSQLI_OPT_MULTI_STATEMENTS, false) - PDO DSN 显式关闭:
mysql:host=localhost;dbname=test;multi_statements=0 - 导出前强制加
LIMIT(哪怕设为 10000),并在 SQL 模板里硬编码,不依赖用户传参 - 记录异常慢查询日志,监控含
SLEEP、BENCHMARK、WAITFOR等关键词的请求
真正难的不是写对一个 prepare,而是所有动态拼接点都得过白名单、转义、限流三关——导出接口尤其容易在“看起来只是展示”的地方放松警惕。











