当前位置: 首页 > news >正文

JDBC常见异常(10)—预编译模式下占位符动态排序字段失效

场景需求

需要根据不同的列进行对应的排序操作,实现动态列名排序 类似🐟动态查询或更新

但是JDBC预编译模式下占位符的排序字段失效

SQL语句

分页查询

select * from (select t.*, rownum rn from(select * from emp order by empno desc) t where rownum <= 5)    where rn > 0;

指定列排序失效

select * from (select t.*, rownum rn from(select * from emp order by ?  desc) t where rownum <= 5)    where rn > 0;

临时解决字符串拼接

  • SQL注入问题
select * from (select t.*, rownum rn from(select * from emp order by "+ empno  +  "  desc) t where rownum <= 5)    where rn > 0;

预编译注入排序列名排序失效

PreparedStatement执行SQL时,如果order by之后的排序字段使用占位符,通过setString设置值的话,会导致排序失效

// 4 SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
PreparedStatement pstmt = conn.prepareStatement(sql);// 如果SQL有?号  参数需要注入
pstmt.setString(1, sortColumnName); // 1 代表 第一个?  从1开始   以此类推

源码

/*** An object that represents a precompiled SQL statement.* <P>A SQL statement is precompiled and stored in a* {@code PreparedStatement} object. This object can then be used to* efficiently execute this statement multiple times.** <P><B>Note:</B> The setter methods ({@code setShort}, {@code setString},* and so on) for setting IN parameter values* must specify types that are compatible with the defined SQL type of* the input parameter. For instance, if the IN parameter has SQL type* {@code INTEGER}, then the method {@code setInt} should be used.** <p>If arbitrary parameter type conversions are required, the method* {@code setObject} should be used with a target SQL type.* <P>* In the following example of setting a parameter, {@code con} represents* an active connection:* <pre>{@code*   BigDecimal sal = new BigDecimal("153833.00");*   PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES*                                     SET SALARY = ? WHERE ID = ?");*   pstmt.setBigDecimal(1, sal);*   pstmt.setInt(2, 110592);* }</pre>** @see Connection#prepareStatement* @see ResultSet* @since 1.1*/
public interface PreparedStatement extends Statement {/*** Sets the designated parameter to the given Java {@code String} value.* The driver converts this* to an SQL {@code VARCHAR} or {@code LONGVARCHAR} value* (depending on the argument's* size relative to the driver's limits on {@code VARCHAR} values)* when it sends it to the database.** @param parameterIndex the first parameter is 1, the second is 2, ...* @param x the parameter value* @throws SQLException if parameterIndex does not correspond to a parameter* marker in the SQL statement; if a database access error occurs or* this method is called on a closed {@code PreparedStatement}*/void setString(int parameterIndex, String x) throws SQLException;... 
}

核心解释

  • 将指定的参数设置为给定的Java{@code String}值。
  • 驱动程序转换此转换为SQL{@code VARCHAR}或{@code LONGVARCHAR}值

原因

PreparedStatement用占位符防止SQL注入的原理是,在为占位符设置值时,会将值转为字符串,然后转义,再将值放入反引号中,放置在占位符的位置上。

因此,当排序字段使用占位符后,原来的排序语句 order by empno(假设排序字段是empno),在实际执行时变成了 order by empno,根据字段排序变成了根据字符串常量值empno排序,导致排序失效,甚至任意的注入数值都不影响前面的查询结果

情况一

使用预编译的数据库操作对象在order by后面设置占位符,再通过pstmt.setString()方法填入参数会导致排序失败

情况二

使用mybatis的时候,在mapper sql映射.xml文件中,在order by 后面使用 #{参数名} 依然会导致排序失败,因为mybatis #{} 使用的是PrepareStatement

解决办法

  • #{}方式传参数只能处理值参数 不能传递表名,字段等参数
  • ${}字符串替换,可以动态处理表名,字段参数

#{}改成${}, #{}是预编译,相当于PrepareStatement;${}是普通字符串的拼接,相当于Statement

但是必须注意SQL注入的风险,对参数做好校验处理

相关文章:

  • Kotlin 类型别名
  • Linux:subshell(子shell)和childprocess(子进程)
  • 工业相机识别电路板元器件:彩色与黑白的区别
  • 束测后台实操文档2-OpenWrt
  • 基于深度学习的模糊认知图方法
  • x264 参考帧管理源码分析
  • 动画:Maya
  • [个人笔记] 记录docker-compose的部署过程
  • 【论文复现|智能算法改进】基于自适应蜣螂算法的无人机三维路径规划方法
  • 使用javacv对摄像头视频转码并实现播放
  • C# 类的深入指南
  • 【JMeter接口自动化】第7讲 Jmeter三个重要组件
  • 第100+9步 ChatGPT文献复现:ARIMA预测百日咳
  • 透视AI技术:探索折射技术在去衣应用中的奥秘
  • 百度地图2
  • CSS3 变换
  • gcc介绍及安装
  • JavaScript学习总结——原型
  • Laravel Telescope:优雅的应用调试工具
  • Python打包系统简单入门
  • springboot_database项目介绍
  • springMvc学习笔记(2)
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 百度小程序遇到的问题
  • 第十八天-企业应用架构模式-基本模式
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 机器学习学习笔记一
  • 记一次用 NodeJs 实现模拟登录的思路
  • 数据科学 第 3 章 11 字符串处理
  • 数组的操作
  • 微信开源mars源码分析1—上层samples分析
  • 与 ConTeXt MkIV 官方文档的接驳
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • $.ajax()方法详解
  • (笔试题)合法字符串
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (简单) HDU 2612 Find a way,BFS。
  • (图)IntelliTrace Tools 跟踪云端程序
  • (转)程序员疫苗:代码注入
  • .CSS-hover 的解释
  • .form文件_一篇文章学会文件上传
  • .gitignore文件_Git:.gitignore
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET 常见的偏门问题
  • .net 后台导出excel ,word
  • .netcore 获取appsettings
  • .Net面试题4
  • .NET应用架构设计:原则、模式与实践 目录预览
  • /ThinkPHP/Library/Think/Storage/Driver/File.class.php  LINE: 48
  • @Valid和@NotNull字段校验使用
  • [ C++ ] STL_list 使用及其模拟实现
  • [Android]常见的数据传递方式
  • [C++基础]-初识模板