【Java代码审计】SQL注入篇

【Java代码审计】SQL注入篇

  • 1.Java执行SQL语句的几种方式
  • 2.Java SQL注入
    • SQL语句参数直接动态拼接
    • 预编译依然采用拼接
    • order by注入
    • %和_模糊查询
    • MyBatis中使用存在风险的语法
  • 3.Java常规注入代码审计思路
  • 4.二次注入代码审计

1.Java执行SQL语句的几种方式

1、JDBC Statement执行SQL语句

java.sql.Statement是Java JDBC下执行SQL语句的一种原生方式,执行语句时需要通过拼接来执行。若拼接的语句没有经过过滤,将出现SQL注入漏洞

驱动注册完成后,实例化Statement对象,SQL语句为select * from users where id = '" + id + "',通过拼接的方式传入id的值

Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);
Statement stmt = conn.createStatement();
String sql = "select * from users where id = '" + id + "'";
log.info("[vul] 执行SQL语句: " + sql);
ResultSet rs = stmt.executeQuery(sql);

2、PreparedStatement执行SQL语句

PreparedStatement是继承statement的子接口,包含已编译的SQL语句。PreparedStatement会预处理SQL语句,SQL语句可具有一个或多个IN参数。IN参数的值在SQL语句创建时未被指定,而是为每个IN参数保留一个问号(?)作为占位符。每个问号的值,必须在该语句执行之前通过适当的setXXX方法来提供。如果是int型则用setInt方法,如果是string型则用setString方法

PreparedStatement预编译的特性使得其执行SQL语句要比Statement快,SQL语句会编译在数据库系统中,执行计划会被缓存起来,使用预处理语句比普通语句更快。PreparedStatement预编译还有另一个优势,可以有效地防止SQL注入攻击,其相当于Statement的升级版

String sql = "select * from users where id = ?";
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1, id);
ResultSet rs = st.executeQuery();

3、MyBatis执行SQL语句

MyBatis是一个Java持久化框架,它通过XML描述符或注解把对象与存储过程或SQL语句关联起来,它支持自定义SQL、存储过程以及高级映射

MyBatis框架底层已经实现了对SQL注入的防御,但存在使用不当的情况下,仍然存在SQL注入的风险

①MyBatis注解存储SQL语句

在这里插入图片描述

②MyBatis映射存储SQL语句

在这里插入图片描述


2.Java SQL注入

SQL语句参数直接动态拼接

在常见的场景下SQL注入是由SQL语句参数直接动态拼接的

例如如下代码:

String sql = "select * from users where id = '" + id + "'";
log.info("[vul] 执行SQL语句: " + sql);
ResultSet rs = stmt.executeQuery(sql);

while (rs.next()) {
    String res_name = rs.getString("user");
    String res_pass = rs.getString("pass");
    String info = String.format("查询结果 %s: %s", res_name, res_pass);
    result.append(info);
}

一个普通的查询,会输出账户名和密码:

http://localhost:8888/SQLI/JDBC/vul1?id=1

在这里插入图片描述

但倘若我们使用引号闭合SQL语句,并使用updatexml构造一个恶意的报错语句,就可以执行任何我们想要执行的SQL命令:

http://127.0.0.1:8888/SQLI/JDBC/vul1?id=1' and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)--%20+

在这里插入图片描述

防御方法,可以采用黑名单过滤的方式(误杀比较严重)

public static boolean checkSql(String content) {
    String[] black_list = {"'", ";", "--", "+", ",", "%", "=", ">", "*", "(", ")", "and", "or", "exec", "insert", "select", "delete", "update", "count", "drop", "chr", "mid", "master", "truncate", "char", "declare"};
    for (String s : black_list) {
        if (content.toLowerCase().contains(s)) {
            return true;
        }
    }
    return false;
}

预编译依然采用拼接

使用PrepareStatement执行SQL语句是因为预编译参数化查询能够有效地防止SQL注入。那么是否能将使用Statement执行SQL语句的方式丢弃掉,使用PrepareStatement执行SQL语句防止SQL注入?

答案是否定的,很多开发者因为个人开发习惯的原因,没有按照PrepareStatement正确的开发方式进行数据库连接查询,在预编译语句中使用错误编程方式,那么即使使用了SQL语句拼接的方式,同样也会产生SQL注入漏洞

例如:

String sql = "select * from users where id = " + id;
log.info("[vul] 执行SQL语句: " + sql);
PreparedStatement st = conn.prepareStatement(sql);
ResultSet rs = st.executeQuery();

while (rs.next()) {
    String res_name = rs.getString("user");
    String res_pass = rs.getString("pass");
    String info = String.format("查询结果%n %s: %s%n", res_name, res_pass);
    result.append(info);
}

一个普通的查询,会输出账户名和密码:

http://127.0.0.1:8888/SQLI/JDBC/vul2?id=1

在这里插入图片描述

但倘若我们使用引号闭合SQL语句,就可以构造恶意的payload来获取用户表的所有数据:

http://127.0.0.1:8888/SQLI/JDBC/vul2?id=2%20or%201=1

在这里插入图片描述

防御方法,是采用占位符的方式执行SQL命令:

public String safe1(String id) {
    String sql = "select * from users where id = ?";
    PreparedStatement st = conn.prepareStatement(sql);
    st.setString(1, id);
    ResultSet rs = st.executeQuery();
}

order by注入

在有些特殊情况下不能使用PrepareStatement,比较典型的就是使用order by子句进行排序。order by子句后面需要加字段名或者字段位置,而字段名是不能带引号的,否则就会被认为是一个字符串而不是字段名。PrepareStatement是使用占位符传入参数的,传递的字符都会有单引号包裹,“ps.setString(1,id)”会自动给值加上引号,这样就会导致order by子句失效

例如:

在这里插入图片描述

因为order by只能使用字符串拼接的方式,当使用“String sql="SELECT*FROM user"+"order by"+id”进行id参数拼接时,就出现了SQL注入漏洞。id参数传入的值为“String id="2 or 1=1"”,因为存在SQL注入漏洞,故当执行完成后会将所有的user表中信息输出

防御方法是执行严格的过滤或使用类似Mybatis的排序映射

%和_模糊查询

在Java预编译查询中不会对%_进行转义处理,而%_刚好是like查询的通配符,如果没有做好相关的过滤,就有可能导致恶意模糊查询,占用服务器性能,甚至可能耗尽资源,造成服务器宕机

如图,当传入的username为“"%user%"”时,通过动态调试发现数据库在执行时并没有将%进行转义处理,而是作为通配符进行查询的

在这里插入图片描述

对于此攻击方式最好的防范措施就是进行过滤,此类攻击场景大多出现在查询的功能接口中,直接将%进行过滤就是最简单和有效的方式

MyBatis中使用存在风险的语法

#{}在底层实现上使用“?”作为占位符来生成PreparedStatement,也是参数化查询预编译的机制,这样既快又安全。${}将传入的数据直接显示生成在SQL语句中,类似于字符串拼接,可能会出现SQL注入的风险

示例:

在这里插入图片描述

${id}不会进行SQL参数化查询,如果传入的数据没有经过过滤就有可能出现SQL注入,设置传入的id的值为“1 and 1=2 union select 1,database(),3”,所以输出了SQL注入后数据库的数据信息:

在这里插入图片描述

另外,在前面order by注入中已经讲到,order by子句不能使用参数化查询的方式,只能使用字符拼接的方式,而在MyBatis中#{}是进行参数化查询的,如果在MyBatis的order by子句中使用#{},则order by子句会失效,所以要使用order by子句只能使用${}

例如,一个风险代码如下:

<select id="orderBy" resultType="com.best.hello.entity.User">
    select * from users order by ${field} ${sort}
</select>

一个正常的排序访问,会输出按照field排序的结果:

http://127.0.0.1:8888/SQLI/MyBatis/vul/order?field=id&sort=desc

在这里插入图片描述

倘若输入恶意的payload,则可以导致注入:

http://127.0.0.1:8888/SQLI/MyBatis/vul/order?field=id&sort=desc,abs(111111)

防御方法是执行严格的过滤或使用Mybatis的排序映射

例如,如下是一个良好的解决order by注入的查询:

<select id="orderBySafe" resultType="com.best.hello.entity.User">
    select * from users
    <choose>
        <when test="field == 'id'">
            order by id desc
        </when>
        <when test="field == 'user'">
            order by user desc
        </when>
        <otherwise>
            order by id desc
        </otherwise>
    </choose>
</select>

还有一种情况,上面提到的%_模糊查询,MyBatis的like子句中使用#{}程序会报错,例如:“select*from users where name like'%#{user}%'”;为了避免报错只能使用${},例如:“select*from users where name like'%${user}%'”;但${}可能会存在SQL注入漏洞,要避免SQL注入漏洞就要进行过滤

例如,下面是一个存在SQL注入的代码:

@Select("select * from users where user like '%${q}%'")
List<User> search(String q);

防御这种类型的攻击,一种有效的方式是强制数据类型,使用 ${} 本身是存在注入的,但由于强制使用Integer或long类型导致注入无效(无法注入字符串)

@Select("select * from users where id = ${id}")
List<User> queryById2(@Param("id") Integer id);

当然,最有效的方式,还是使用 #{} 安全编码,不过为了语法的正确性,要采用CONCAT函数进行拼接语句

@Select("select * from users where user like CONCAT('%', #{user}, '%')")
List<User> queryByUser(@Param("user") String user);

另外,MyBatis的in子句中使用#{}参数化查询,会将“select * from users where name in (#{user})”转变为“select * from users where name like (''user1','user2','user3','user4'')”,这样把“‘user1’,‘user2’,‘user3’,‘user4’”当作一个整体,偏离了原来的程序设计逻辑,无法查到数据,所以也存在上述的问题


3.Java常规注入代码审计思路

我们可以总结出下面这些常见的关键字,通过这些关键字便可快速地定位到SQL语句的附近,进而进行有针对性的审计:

在这里插入图片描述

例如我们搜索Statement,发现如下代码片段:

 Statement stmt = conn.createStatement();
 String sql = "select * from users where id = '" + id + "'";
 log.info("[vul] 执行SQL语句: " + sql);
 ResultSet rs = stmt.executeQuery(sql);

id是用户传入的参数,现在我们回看source点,看看对于id有没有什么过滤,以及过滤能否绕过:

追踪我们controller的视图,查看MvcConfig类:

在这里插入图片描述

进入视图文件,发现id是直接传入的方式,没有任何的过滤和全局过滤器:

在这里插入图片描述

注入产生!


4.二次注入代码审计

1、与常规注入一样,通过搜索SQL关键字定位至存在SQL语句的文件

在这里插入图片描述

2、跟进“UserMapper.java”文件,可以发现:其中定义了大量SQL语句,但大多数使用了#号的安全写法。通过搜索可以发现:以下语句使用了不安全的$

在这里插入图片描述

3、通过搜索调用栈,在UserService.java中可以找到其对应的调用

在这里插入图片描述

4、通读代码可以发现其逻辑为:从session中取出username,随后拼入SQL语句进行查询。我们接着查找session的调用,便能找到其赋值依据。最终在login逻辑中成功地找到了session的赋值过程

在这里插入图片描述

5、这里可以看到username的值来源于user.getUsername(),也就是说,username的值是通过登录时输入用户名获取的,由于前面存在if逻辑判断,因此此处取到的应是成功登录后的用户名。那么我们可以接着寻找注册逻辑,以便对漏洞进行利用

在这里插入图片描述

6、注册逻辑直接调用UserMapper进行入库操作,并没有对用户名进行过滤。同时入库时采用的是#号的安全写法,最后会通过预编译执行SQL语句

在这里插入图片描述

7、这里存在的注入为二次注入,而我们想要触发该漏洞则需先注册一个存在注入语句的用户名进行登录,随后通过触发info逻辑进行二次注入。通过查看逻辑可以知道:info是通过路由/info进行触发的

在这里插入图片描述

8、首先,注册账号名为“'union select user(),2,3#”的用户名

在这里插入图片描述

9、登录后触发info逻辑

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/247661.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java综合实验-图书管理系统

二、实验项目内容&#xff08;实验题目&#xff09; 1. 使用Java编程语言进行实验。 2. 采用面向对象的思想进行系统设计。 3. 实现基本的图书管理功能&#xff0c;包括添加图书、删除图书、查询图书信息等。 4. 要求有良好的用户交互界面。 步骤参考&#xff1a; 步骤一…

ChatGPT使用:一个发包机器人的提示词

发包机器人&#xff1a; 设想&#xff1a;目前项目组有n条打包线会输出多个包&#xff0c;用户想获取最新的包是比较困难的&#xff0c;难点在于 1. 分支多&#xff1a;trunk&#xff0c;release&#xff0c;outer等&#xff0c;至少有3个分支&#xff1b; 2. 多平台&#x…

数据分析的基本步骤

了解过数据分析的概念之后&#xff0c;我们再来说下数据分析的常规步骤。 明确目标 首先我们要确定一个目标&#xff0c;即我们要从数据中得到什么。比如我们要看某个指标A随时间的变化趋势&#xff0c;以期进行简单的预测。 数据收集 当确定了目标之后&#xff0c;就有了取…

音乐制作软件Ableton Live 11 mac功能特点

Ableton Live 11 mac是一款数字音频工作站软件&#xff0c;用于音乐制作、录音、混音和现场演出是一款流行的音乐制作软件。 Ableton Live 11 mac特点和功能 Comping功能&#xff1a;Live 11增加了Comping功能&#xff0c;允许用户在不同的录音轨道上进行多次录音&#xff0c;…

前端离开后端就不能开发项目了吗?

前端离开后端就不能开发项目了吗&#xff1f; 经常在技术社区中看到后端个个都能代替前端&#xff0c;前端却代替不了后端&#xff01; 后端有多牛&#xff0c;前端有多菜&#xff01;嗯.......事实真的如此吗&#xff1f;前端一个人在没有服务器、数据库的情况下到底能不能开发…

怎么把图片转文字?这几个图片转文字方法一定要知道!

怎么把图片转文字&#xff1f;无论是从书籍、网络还是社交媒体上&#xff0c;我们经常需要从图片中提取文字来进行复制、编辑或翻译。手动操作耗时耗力&#xff0c;效率低下&#xff0c;那么怎么把图片转文字呢&#xff1f;今天我将介绍三种不同的方法来实现图片转文字。 图片转…

Python之time模块详解

python3中time模块的用法及说明 python中&#xff0c;导入time模块使用的命令是 import time 可以使用以下命令查看time模块内置的能够使用的方法&#xff1a; dir(time) 可以使用以下命令查看time模块中每个内置方法的说明&#xff1a; help(time.time_method) 比如time模块下…

elementui + vue2实现表格行的上下移动

场景&#xff1a; 如上&#xff0c;要实现表格行的上下移动 实现&#xff1a; <el-dialogappend-to-bodytitle"条件编辑":visible.sync"dialogVisible"width"60%"><el-table :data"data1" border style"width: 100%&q…

Python之random模块详解

python的random模块 random模块是python中一个生成随机数的模块。 random不是python解释器内置的模块。 导入random模块的方法是&#xff1a; import random 如果只使用random模块中的单个方法的话&#xff0c;也可以使用 from random import method_name 例如&#xff1a; …

中医处方软件西医电子处方系统,一键生成处方单可设置配方模板教程

一、前言 有的诊所是中医和西医都有&#xff0c;医师是全科医师&#xff0c;那么所使用的软件既要能开中药处方也要能开西药处方&#xff0c;而且可以通过一键生成配方&#xff0c;则可以节省很多时间。 下面就以 佳易王诊所卫生室电子处方为例说明 如上图&#xff0c;如果是…

Python Pandas 如何给DataFrame增加一行/多行 数据(第6讲)

Python Pandas 如何给DataFrame增加一行/多行 数据(第6讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ…

【Linux】进程周边004之进程的调度与切换(领略Linux系统进程调度算法的神奇)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.进程切换 2.进程调度 2.…

十四 动手学深度学习v2计算机视觉 ——转置矩阵

文章目录 基本操作填充、步幅和多通道再谈转置卷积不填充&#xff0c;步幅为1填充为p&#xff0c;步幅为1填充为p&#xff0c;步幅为s 基本操作 填充、步幅和多通道 填充&#xff1a; 与常规卷积不同&#xff0c;在转置卷积中&#xff0c;填充被应用于的输出&#xff08;常规卷…

ospf 知识总结

ospf 知识总结 一、ospf的概念 - 开放式最短路径优先协议&#xff0c;是广泛使用的一种动态路由协议&#xff0c;它属于链路状态路由协议&#xff0c;是一个内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在单一自治系统&#xff08;AS&#xff09;内决策路由。 - …

OpenHarmony - 应用开发入门指南

一、了解OpenHarmony OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目, 目标是面向全场景、全连接、全智能时代, 搭建一个智能终端设备操作系统的框架和平台, 促进万物互联产业的繁荣发展。 开放原子开源基金会&#xff1a; 由阿里巴巴、百度、华…

数据分析为何要学统计学(7)——什么问题适合使用t检验?

t检验&#xff08;Students t test&#xff09;&#xff0c;用于通过小样本&#xff08;样本容量n < 30&#xff09;对总体均值水平进行无差异推断。 t检验要求样本不能超过两组&#xff0c;且每组样本总体服从正态分布&#xff08;对于三组以上样本的&#xff0c;要用方差…

yolov8实战第二天——yolov8训练结果分析(保姆式解读)

yolov8实战第一天——yolov8部署并训练自己的数据集&#xff08;保姆式教程&#xff09;-CSDN博客 我们在上一篇文章训练了一个老鼠的yolov8检测模型&#xff0c;训练结果如下图&#xff0c;接下来我们就详细解析下面几张图。 一、混淆矩阵 正确挑选&#xff08;正确&#…

CompletableFuture原理解析

文章目录 一、 Callable、Future介绍1. 简介2. 底层原理 二、 FutureTask介绍1. 简介2. 底层原理 三、CompletionService1. 简介2. 原理3. 源码分析4. 总结 四、CompletableFuture1. 简介2. 案例3. 源码分析 一、 Callable、Future介绍 1. 简介 Future 是用于表示异步计算结果…

美客多、亚马逊卖家借助自养号测评增加销量和提升店铺排名的方法

在跨境电商的浩瀚领域中&#xff0c;成功打造并运营一个具有竞争力的店铺如同航行在大海中的一艘船&#xff0c;需要精准的航向和持续的努力。亚马逊&#xff0c;美客多这个广袤的电商平台&#xff0c;如同一片繁星点点的海域&#xff0c;需要卖家们以巧妙的策略和专注的态度航…

解锁知识的新大门:自建知识付费小程序的技术指南

在数字化时代&#xff0c;知识付费小程序的崛起为创作者和学习者提供了全新的学习和分享方式。本文将以“知识付费小程序源码”为关键词&#xff0c;从技术角度出发&#xff0c;为你展示如何搭建一个独具特色的知识付费平台。 步骤1&#xff1a;选择适用的知识付费小程序源码…