spring boot 连接 oracle 防 sql 注入本质与 mysql/postgresql 一致,核心是严格使用 preparedstatement 绑定参数值,禁用字符串拼接;表名、列名、order by 字段等结构信息须白名单校验,原生 sql 中 in 子句需固定占位符或改用 namedparameterjdbctemplate。

Spring Boot 连接 Oracle 时防 SQL 注入,和用 MySQL 或 PostgreSQL 没本质区别——Oracle 同样支持 PreparedStatement,JDBC 驱动(ojdbc8+)完整兼容预编译协议。关键不是数据库类型,而是你**怎么写 SQL、怎么传参、怎么构造查询结构**。
用 JdbcTemplate 时必须走 ? 占位符,禁用字符串拼接
Oracle 对参数绑定无特殊要求,但开发者常因“习惯写 PL/SQL”误用字符串拼接。只要拼了,就破防。
- ❌ 错误:直接拼接表名或条件值 ——
jdbcTemplate.query("SELECT * FROM " + tableName + " WHERE id = " + id, ...) - ✅ 正确:只对「值」用
?,且参数单独传 ——jdbcTemplate.query("SELECT name, email FROM users WHERE status = ? AND dept_id IN (?, ?)", new Object[]{"ACTIVE", 101L, 102L}, rowMapper) - ⚠️ 注意:
IN动态列表不能靠拼问号个数解决,否则会触发Invalid argument value: java.io.NotSerializableException;应改用NamedParameterJdbcTemplate+:ids绑定
@Query(nativeQuery = true) 必须用 ?1 / :param,绝不可拼字符串
Oracle 原生 SQL(比如调用 DECODE、ROWNUM 分页、自定义函数)常被写进 @Query(nativeQuery = true),这里最容易翻车。
- ❌ 错误:
@Query(value = "SELECT * FROM emp WHERE name = '" + name + "'", nativeQuery = true)—— 直接执行字符串拼接 - ✅ 正确(位置参数):
@Query(value = "SELECT * FROM emp WHERE dept_id = ?1 AND salary > ?2", nativeQuery = true),方法签名带Long deptId, BigDecimal minSalary - ✅ 正确(命名参数):
@Query(value = "SELECT * FROM emp WHERE status = :status", nativeQuery = true),配合@Param("status") String status - ⚠️ 注意:Oracle 不支持原生 SQL 中
WHERE id IN :ids这种写法,Hibernate 无法展开;必须手动写固定长度占位符,如IN (?1, ?2, ?3),再传三参数
表名、列名、ORDER BY 字段必须白名单校验
JDBC 的 PreparedStatement 只绑定「值」,不绑定「结构」。Oracle 允许动态表名(EXECUTE IMMEDIATE),但 Spring 不提供这种能力——你手写的任何字符串拼接,都绕过了所有防护。
- ❌ 错误:
"SELECT * FROM " + userProvidedTable + " ORDER BY " + sortField - ✅ 正确:用枚举或配置项限定可选值,例如:
if (!Set.of("users", "orders", "products").contains(tableName)) throw new IllegalArgumentException(); - ✅ 替代方案:用
Sort.by(Sort.Direction.ASC, "username")构造Pageable,交给 Spring Data JPA 处理排序,它会安全转成ORDER BY username ASC - ⚠️ 注意:Oracle 的双引号标识符(
"User_Name")也需校验,不能直接反射进 SQL;白名单应包含合法的带引号字段名
别信“Oracle 自带过滤”,请求层 Filter 覆盖不到 JSON 和文件上传
有人在 Oracle 连接串里加 ?oracle.jdbc.autoCommitSpecCompliant=false 或依赖 Oracle Wallet,以为能防注入——没用。SQL 注入发生在应用层构造语句阶段,不是连接认证阶段。
- ❌ 误区:以为 WebFilter 扫
getParameterMap()就能拦住所有攻击 —— 它拿不到 POST body 的 JSON、multipart/form-data中的字段、Header 里的 token - ✅ 正确做法:若真要加过滤,用
@WebFilter(urlPatterns = "/*")+HttpServletRequestWrapper重写getInputStream()和getReader(),统一解析并清洗原始字节流 - ⚠️ 关键提醒:Filter 必须加
@Component,且加载顺序要在SecurityFilterChain之前;健康检查路径(如/actuator/health)必须 exclude,否则监控失灵
真正难的不是让一条 SELECT 语句防注入,而是确认你在处理 multipart/form-data 文件上传里的文本字段、WebSocket 消息体、或者 Feign Client 转发的原始 JSON 时,有没有哪一层悄悄把用户输入拼进了 SQL 字符串里——这种漏洞往往上线半年才暴露。











