Druid防SQL注入的关键是确保MyBatis使用#{}走PreparedStatement预编译,而非依赖其wall filter;必须显式启用预编译、禁用config filter、严格区分#{}与${}、配合白名单校验动态元素,并配置合理的PreparedStatement缓存及最小权限数据库账户。

Druid 连接池开启 PreparedStatement 预编译支持
MyBatis 默认使用 Statement 执行 SQL 时,拼接参数会直接落入 SQL 字符串,极易触发注入;Druid 必须确保底层驱动走 PreparedStatement 流程,否则配置再严也没用。
关键不是 Druid 自己“防注入”,而是它要配合 MyBatis 正确传递参数、不绕过预编译。检查你的 druid.properties 或 Spring Boot application.yml 中是否显式启用了预编译:
-
druid.connection-init-sql=SELECT 1—— 确保连接建立后能执行简单语句(验证驱动兼容性) -
druid.use-global-datasource-stat=false—— 避免统计开关干扰 PreparedStatement 缓存行为 - 必须关闭
druid.filter.config.enabled=false(如果误启了 config filter,可能拦截掉 MyBatis 的#{}参数解析)
MyBatis 的 #{} 和 ${} 必须严格区分使用场景
Druid 不干预 MyBatis 的参数占位符解析逻辑,真正决定是否安全的是你写 SQL 时选 #{} 还是 ${}。前者走 PreparedStatement 绑定,后者纯字符串替换——哪怕 Druid 开了所有防护,${tableName} 依然高危。
常见错误现象:org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'xxx' in class java.lang.String,往往是因为该用 #{} 却写了 ${},又没传对 Map 结构。
- 动态表名、列名、ORDER BY 字段:只能用
${},但必须配合白名单校验(比如枚举或正则匹配),不能直接透传用户输入 - WHERE 条件值、INSERT 值、UPDATE SET 值:一律用
#{} - MyBatis-Plus 的
QueryWrapper默认走#{},但apply("status = ${status}")是危险的,应改用eq("status", status)
Druid 内置防火墙 filter 的实际拦截能力很有限
Druid 提供的 stat、wall、config 三类 filter 中,只有 wall 声称防 SQL 注入,但它本质是基于关键词+语法规则的文本扫描,不是执行时上下文分析。
它拦不住合法 SQL 中嵌套的注入,比如 SELECT * FROM user WHERE name = #{name} AND 1=1 /* */ OR 1=1 --(注释绕过)、或 Base64 编码后的 payload。更麻烦的是,它默认规则过于宽松,连 UNION SELECT 都不拦截。
- 启用前先确认
druid.wall.enabled=true,且加载了druid.wall.config-file=wall-filter-config.json - 务必自定义规则文件,把
selectUnionAllow、selectAllColumnAllow、conditionAndTrueAllow全部设为false - 注意:开启 wall filter 后,MyBatis 的批量插入(
<foreach>)可能因解析失败报com.alibaba.druid.sql.parser.ParserException,此时需在 SQL 中避免复杂括号嵌套或换行
真正起作用的是参数绑定链路 + 输入校验 + 最小权限 DB 账户
Druid 的 wall filter 是最后一道弱防线,别把它当核心手段。SQL 注入防御的关键节点其实在更上游:MyBatis 是否始终用 #{}、Controller 层是否对路径变量/查询参数做过滤、数据库账户是否只拥有必要表的 SELECT/INSERT 权限。
一个常被忽略的点:Druid 的 maxPoolPreparedStatementPerConnectionSize 如果设得太小(比如默认 -1 或 0),会导致 PreparedStatement 缓存失效,频繁重编译,不仅性能差,还可能让某些驱动回退到 Statement 模式。
- 建议设为 20–50(根据 SQL 数量调整),并监控 Druid 控制台的
PreparedStatementCacheHitCount - Web 层校验不能只靠前端 JS,后端必须用
@Pattern或自定义注解约束参数格式(如手机号、订单号) - 数据库账户禁止授予
FILE、EXECUTE、CREATE FUNCTION等高危权限
最复杂的其实是动态 SQL 场景下的白名单设计——比如多字段模糊搜索,既要支持用户选字段,又不能放开任意列名。这时候得用 Map










