最常见错误是用#{}替换所有${},真正修复需分场景:like查询须用数据库函数拼接(如MySQL的concat('%',#{name},'%')),order by动态字段需Java层白名单校验,动态表名列名必须通过枚举或上下文映射且账号权限最小化。

直接用 #{} 替换所有 ${} 是最常见但错误的“修复”——它在 ORDER BY、TABLE NAME、COLUMN NAME 等场景会直接报错或失效。真正的修复必须分场景处理,不能一刀切。
like 模糊查询时为什么不能写 '%${name}%'?
因为 ${} 会把用户输入原样拼进 SQL,攻击者输入 admin' OR 1=1 -- 就变成:WHERE username LIKE '%admin' OR 1=1 --%',注释掉后续条件,查出全部用户。
正确做法是让数据库函数处理拼接,保持参数走预编译:
- MySQL 推荐用
concat('%', #{name}, '%'),#{name}仍被当作安全参数绑定 - PostgreSQL 用
'%' || #{name} || '%' - Oracle 用
'%' || #{name} || '%'或CONCAT(CONCAT('%', #{name}), '%') - 绝对不要在 Java 层手动拼
"%" + name + "%"再传给${}—— 这等于裸奔
order by 动态字段怎么防注入?
ORDER BY #{column} 会加引号变成 ORDER BY 'user_name',语法错误;而 ORDER BY ${column} 又完全暴露。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
必须做白名单校验,且校验逻辑不能放在 XML 里:
- Java 方法中先判断
column是否属于预设字段列表,如Arrays.asList("id", "username", "create_time") - 只允许传入字段名(不是表达式),拒绝
id, (SELECT SLEEP(5))这类恶意内容 - XML 中仍用
ORDER BY ${column},但${column}的值已由 Java 层严格过滤过 - 别信“前端传个数字索引再映射”,索引本身没校验照样可越界或伪造
动态表名/列名必须用 ${} 时如何兜底?
这类需求无法避免 ${},但风险极高。修复核心是:不信任任何外部输入,只接受内部可控标识。
- 禁止从 HTTP 参数、JSON body、URL path 直接取值赋给
${tableName} - 改用枚举或配置项映射:比如前端传
type=user,Java 层查Map.of("user", "t_user_v2")得到真实表名 - 若需多租户分表,表名应来自租户上下文(如 ThreadLocal 中的 tenantId),而非用户请求
- 数据库账号权限最小化:该应用账号只对白名单表有 SELECT 权限,删库语句直接被 DB 拒绝
最易被忽略的一点:<foreach></foreach> 标签里如果用了 ${item} 而非 #{item},哪怕整个语句其他地方都安全,也会在 IN 列表中触发注入。凡是涉及用户数据参与 SQL 结构的位置,都要逐行检查占位符类型。

















