核心是坚持参数化查询+输入约束+权限隔离;oracle不改变注入原理,但execute immediate、动态表名等特性使漏洞更隐蔽,必须用preparedstatement并白名单校验不可参数化的表名,禁用${}拼接和高危权限。
java中防止oracle sql注入,核心不是换数据库驱动,而是坚持参数化查询 + 输入约束 + 权限隔离。oracle本身不改变sql注入原理,但其特性(如绑定变量语法、execute immediate、动态表名支持)会让某些漏洞更隐蔽。
必须用 PreparedStatement,别信“Oracle能自动转义”
Oracle JDBC驱动对PreparedStatement的实现是可靠的,但前提是真正用了它——不是写了PreparedStatement接口却传入拼接好的SQL字符串。
- ✅ 正确:SQL模板里只出现
?占位符,所有用户输入走setString()、setLong()等方法 - ❌ 错误:
"SELECT * FROM " + tableName + " WHERE id = ?"——表名不能参数化,这种拼接照样触发注入 - ⚠️ 注意:
setString()对单引号、分号、--等字符不做“过滤”,它靠的是数据库层面的绑定机制,不是字符串替换
Oracle特有场景:动态表名/列名怎么安全处理
当业务真需要运行时决定查哪张表(比如分表日志),?无法绑定表名。这时不能放任用户输入直接进SQL,必须白名单校验。
- 把允许的表名列在配置或枚举里:
EnumSet.of(LogTable.USER_202606, LogTable.ORDER_202606) - 用
switch或Map映射用户输入到预定义表名,拒绝任何未登记的值 - 绝对不用
String.format("SELECT * FROM %s", userInputTable)或正则替换“非法字符”——Oracle支持Unicode变体、注释嵌套,黑名单永远漏
警惕 Oracle 的 EXECUTE IMMEDIATE 和原生 SQL
MyBatis里用${}、JDBC里调CallableStatement执行PL/SQL块,都可能绕过PreparedStatement保护。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
-
${}在MyBatis中等于字符串拼接,仅限硬编码值(如${@TBL_PREFIX@}_users),绝不能绑定request.getParameter("table") - PL/SQL中用
EXECUTE IMMEDIATE 'SELECT ...' || user_input——这和Java里拼SQL一样危险,必须先校验再拼 - Hibernate用
@Query(value = "...", nativeQuery = true)时,也必须确保所有变量都通过:param绑定,而非+拼接
Oracle权限与连接配置要收紧
即使某处漏了防护,最小权限也能卡住攻击者横向移动。
- 应用数据库账号禁用
DROP ANY TABLE、ALTER SYSTEM、UTL_HTTP等高危权限 - JDBC URL加
oracle.jdbc.useFetchSizeWithLongColumn=true&allowMultiQueries=false(后者防堆叠注入) - 生产环境关闭
show_sql=true和详细Oracle错误(如ORA-00942暴露表名)
最易被忽略的点是:开发者常以为“用了Oracle就比MySQL安全”,其实Oracle的EXECUTE IMMEDIATE、自治事务、高级队列表等功能反而给注入提供更多入口。防御重点不在数据库侧,而在Java代码是否把用户输入当数据而非代码来对待——这个原则,和MySQL、PostgreSQL完全一致。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










