后台管理系统最易被sql注入攻破的是带搜索、筛选、导出功能的get接口,因其常拼接sql且校验缺失;防御须全覆盖所有动态sql路径,严控参数化、白名单与最小权限。

后台管理系统最容易被SQL注入攻破的地方,不是登录页,而是那些带搜索、筛选、导出功能的接口——尤其是用 GET 传参的列表页,比如 /api/users?name=admin' OR '1'='1 这类请求,一发就中。
为什么后台系统特别容易中招
后台接口往往追求开发速度,大量使用拼接 SQL 的写法;同时权限集中、数据敏感、接口暴露多,攻击者一旦拿下一个低权限账号(比如运营人员),就能顺着接口批量拖库。常见风险点包括:
- 管理员手写的 DAO 层 SQL,直接用
String.format或+拼接WHERE条件 - 前端传来的
sort、order参数没校验,被用来拼接ORDER BY username ASC—— 攻击者可填入id; DROP TABLE users; - 导出 Excel 的接口接收
ids数组,后端用"IN (" + ids.join(",") + ")"构造条件,没做类型强转 - 日志查询、审计报表等“辅助功能”接口,因非核心业务常被忽略安全审查
参数化查询必须覆盖所有执行路径
光在登录、注册处用了 PreparedStatement 或 #{} 不够——只要有一处漏网,整套防御就形同虚设。重点盯死这三类语句:
-
SELECT带动态WHERE:所有字段名、值都必须参数化,连status = ?这种也不能写成"status = " + status -
UPDATE的SET子句:字段名不能由用户控制(如SET ? = ?是错的),只能固定字段,值用参数 -
IN查询:不要手动拼逗号分隔字符串,改用数据库驱动支持的批量参数(如 MyBatis 的<foreach></foreach>,或 JDBC 的setArray())
示例(Java + MyBatis):
✘ 错误:SELECT * FROM orders WHERE user_id IN (${ids})
✔ 正确:SELECT * FROM orders WHERE user_id IN <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
绕过参数化的常见陷阱
有些写法看似参数化,实则仍可注入:
-
ORDER BY字段名不能参数化(JDBC 不允许对列名占位),必须白名单校验:if (!Arrays.asList("created_at", "amount", "status").contains(sortField)) throw new IllegalArgumentException(); -
LIMIT和OFFSET必须转为整数再传参,防止1; DROP TABLE...注入:stmt.setInt(1, Math.max(0, Integer.parseInt(limitStr))) - MyBatis 中误用
${}替代#{}:前者是字符串替换,后者才是预编译参数,任何地方出现${xxx}都要立刻排查 - ORM 的原生 SQL 方法(如 Hibernate 的
createSQLQuery、Django 的extra())同样需要手动参数化,框架不自动兜底
最小权限 + 输入白名单是最后防线
即使代码层全做了参数化,数据库账户权限过大,依然可能被利用:
- 后台应用连接数据库的账号,禁止授予
DROP、CREATE、ALTER、LOAD_FILE等权限 - 只开放
SELECT、INSERT、UPDATE、DELETE,且仅限业务表(别给information_schema查询权) - 对所有字符串输入加白名单过滤:如状态字段只允许
"pending"、"done"、"canceled",其余直接拒掉 - 数字类参数强制转
int或long,空值或非数字直接 400,不进 DB 层
真正危险的从来不是“会不会写参数化”,而是“有没有漏掉某个导出接口的 ID 列表拼接”,或者“那个新加的模糊搜索框,到底走的是 #{keyword} 还是 ${keyword}”。后台系统的防护,本质是把每一处动态 SQL 都当作雷区来对待。











