Spring Security 不防 SQL 注入,它工作在 Web 层,而 SQL 注入发生在数据访问层;真正有效的防护是:MyBatis 用 #{}、JPA 用命名参数、JDBC 用 PreparedStatement,并最小化数据库权限。

Spring Security 本身不参与 SQL 注入防护,指望它自动拦截 ' OR '1'='1 这类输入是无效的——它根本不会看到 SQL 语句,更不会解析或改写你的 SELECT 或 INSERT。
Spring Security 和 SQL 防御根本不在同一层
Spring Security 是 HTTP 请求生命周期的守门人,工作在 Web 层(Filter → Servlet → Controller),而 SQL 注入发生在数据访问层(DAO/Repository → JDBC/MyBatis → 数据库)。两者之间隔着至少三层:业务逻辑、ORM 封装、数据库驱动。
常见错误现象包括:
- 在
@RestController里用String.format("WHERE name = '%s'", name)拼 SQL,再配十个SecurityFilterChain也拦不住 - 以为开启
http.csrf().disable()或配置了authorizeHttpRequests就等于“防注入” - 把 Druid 的
wall防火墙当成万能盾,却忽略其仅对部分语法有效、且默认不启用
真正起作用的只有三件事:
- MyBatis 中严格使用
#{},禁用${}(后者直接字符串替换) - JPA
@Query必须用命名参数(:name),绝不用+拼接 - JDBC 原生调用必须走
PreparedStatement,Statement+ 字符串拼接是红线
Druid wall 配置能补什么,不能补什么
Druid 的 wall 过滤器是数据库连接池侧的语法级拦截,属于“最后一道辅助防线”,不是替代方案。
它能识别并拒绝明显危险的 SQL 片段,比如:
-
UNION SELECT(除非你明确允许) -
DELETE FROM user(若配置了delete-allow: false) -
DROP TABLE、EXEC xp_cmdshell等高危指令
但它无法防御:
- 参数化查询中被绕过的逻辑漏洞(如
WHERE id IN (#{idList})中idList是用户可控 List,仍可能触发堆叠注入) - 动态表名/列名场景(
FROM ${tableName}),此时#{}失效,必须靠白名单校验 - 存储过程调用中的参数污染(
CALL proc(?)安全,但CALL ${procName}(?)不安全)
示例配置(application.yml):
spring:
datasource:
druid:
filters: stat,wall
wall:
config:
select-allow: true
delete-allow: false
drop-table-allow: false
condition-and-allow: true
为什么数据库账号权限最小化比任何代码都管用
哪怕 ORM 层漏了一处 ${},只要数据库账号没有 DROP、ALTER、跨库 SELECT 权限,攻击者最多只能读当前表的几列数据,无法拖库、无法写 shell、无法提权。
真实踩坑点:
- 开发环境用
root账号连 MySQL,测试通过就上线,结果某次日志打印把完整 SQL 泄露到 ELK,攻击者顺藤摸瓜构造注入 - 一个只查用户信息的接口,数据库账号却有
INSERT INTO log_table权限,被用来盲注回显 - PostgreSQL 中未限制
pg_read_file()等高危函数,导致文件读取+SQL注入组合利用
建议按接口粒度分配权限:
-
GET /user/{id}→ 只授SELECTonuser表 -
POST /order→ 授INSERTonorder,SELECTonproduct(只读) - 禁用
publicschema 的USAGE,显式指定 schema 名
Spring Security 唯一能协同 SQL 防御的地方:日志与监控闭环
它不拦 SQL,但可以帮你更快发现异常行为。关键不是“防”,而是“感知+响应”。
实操建议:
- 在
SecurityFilterChain中记录可疑请求特征:request.getQueryString()含UNION、SELECT、单引号连续出现 ≥3 次 - 结合 Spring AOP,在 DAO 方法执行前记录慢查询或异常 SQL(如
org.springframework.dao.DataIntegrityViolationException可能暗示注入试探) - 将
DataSource包装为代理,对执行的 SQL 做简单模式匹配(非正则,避免性能损耗),命中即发告警,不阻断
注意:这类检测必须轻量,否则会拖垮高并发接口;也不要试图在 Filter 里重写 SQL——那不是它的职责,也极易引入新 bug。
最常被忽略的一点:SQL 注入的初始入口往往不是业务 API,而是 Actuator 端点、Swagger UI、H2 Console 这类调试接口。它们默认暴露在生产环境,又常被遗忘在安全扫描之外。

















