JAVA代码审计之SQL注入代码审计

news2024/11/30 13:27:37

前言

SQL注入漏洞是对数据库进行的一种攻击方式。其主要形成方式是在数据交互中,前端数据通过后台在对数据库进行操作时,由于没有做好安全防护,导致攻击者将恶意代码拼接到请求参数中,被当做SQL语句的一部分进行执行,最终导致数据库被攻击。可以说所有可以涉及到数据库增删改查的系统功能点都有可能存在SQL注入漏洞。虽然现在针对SQL注入的防护层出不穷,但大多情况下由于开发人员的疏忽或特定的使用场景,还是会存在SQL注入漏洞的代码。

环境搭建

首先创建相关项目,源码这儿较多,后台私信即可获取演示案例源码

首先是根据提示,创建一个名为security的数据库,并想数据库中写入相关数据。这里我用的是phpStudy的mysql数据库

接着使用Navicate创建相关数据

DROP DATABASE IF EXISTS security;
CREATE DATABASE security;
USE security;

CREATE TABLE users (
    id INT(3) NOT NULL AUTO_INCREMENT,
    username VARCHAR(20) NOT NULL,
    password VARCHAR(20) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE emails (
    id INT(3) NOT NULL AUTO_INCREMENT,
    email_id VARCHAR(30) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE uagents (
    id INT(3) NOT NULL AUTO_INCREMENT,
    uagent VARCHAR(256) NOT NULL,
    ip_address VARCHAR(35) NOT NULL,
    username VARCHAR(20) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE referers (
    id INT(3) NOT NULL AUTO_INCREMENT,
    referer VARCHAR(256) NOT NULL,
    ip_address VARCHAR(35) NOT NULL,
    PRIMARY KEY (id)
);

INSERT INTO users (id, username, password) VALUES 
(1, 'Dumb', 'Dumb'), 
(2, 'Angelina', 'I-kill-you'), 
(3, 'Dummy', 'p@ssword'), 
(4, 'secure', 'crappy'), 
(5, 'stupid', 'stupidity'), 
(6, 'superman', 'genious'), 
(7, 'batman', 'mob!le'), 
(8, 'admin', 'admin');

INSERT INTO emails (id, email_id) VALUES 
(1, 'Dumb@dhakkan.com'), 
(2, 'Angel@iloveu.com'), 
(3, 'Dummy@dhakkan.local'), 
(4, 'secure@dhakkan.local'), 
(5, 'stupid@dhakkan.local'), 
(6, 'superman@dhakkan.local'), 
(7, 'batman@dhakkan.local'), 
(8, 'admin@dhakkan.com');

接着连接数据库,这里数据库是本地的,可以直接连接

然后启动即可。访问目标地址,搭建成功

http://127.0.0.1:7089/sqli/jdbc/dynamic?id=2

漏洞分析

jdbc中的SQL注入

动态拼接

SQL语句动态拼接导致的SQL注入漏洞是先前最为常见的场景。其主要原因是后端代码将前端获取的参数动态直接拼接到SQL语句中使用java.sql.Statement执行SQL语句从而导致SQL注入漏洞的出现。

两个关键点如下:动态拼接参数、使用java.sql.Statement执行SQL语句

Statement:对象用于执行一条静态的 SQL 语句并获取它的结果。
createStatement():创建一个 Statement 对象,之后可使用executeQuery()方法执行SQL语句。
executeQuery(String sql)方法:执行指定的 SQL 语句,返回单个 ResultSet 对象。	

演示案例如下:

我们来到JdbcDynamicContriller下,相关代码已经做了详细注释,这里不一一讲解。

我们主要观察以下代码:

// 创建Statement对象用于执行SQL查询
Statement statement = conn.createStatement();
//动态拼接字符串
String sql = "select * from users where id = '" + id + "'";
// 执行SQL查询并返回结果集
ResultSet rs = statement.executeQuery(sql);

id是通过请求参数传入来获取的,这里直接进行了拼接,并且是通过创建Statement对象用于执行SQL查询,executeQuery执行查询语句,这里就存在sql注入。实际演示看看。

访问以下地址可以看到正常显示

http://127.0.0.1:7089/sqli/jdbc/dynamic?id=2

后面加一个单引号,可以看到报错了

那再加一个,输入 2'' 试试呢

可以看到正常显示了,为什么会这样子呢?回到源码来进行分析一下,这里修改源码返回的内容,将返回的result修改成sql语句。

接着访问源地址,可以看到完整的sql语句了

可以看到,造成输入2''还可以正常显示的原因是,字符串造成了拼接,我们输入的2'拼接在sql语句中,闭合了单引号,因而造成的sql注入,这里简单演示一下注入流程。我就简单查询一下数据库版本吧。

这里通过order by语句已知有三列,我们数据一下语句,查询数据库版本。

' union select 1,2,version()--+

但是存在一种情况,即使是拼接,也不会造成sql注入(目前还未发现绕过方法),那就是限制输入的类型为int,我们输入其他的就会报错。

正常输入,可以看到显示正常

但是一旦输入其他的,非数字的情况,就会报错。这种情况就基本不存在sql注入了。

错误的预编译

在动态拼接中是使用Statement执行SQL语句。如果使用PreparedStatement预编译参数化查询是能够有效防止SQL注入的。但如果没有正确的使用PreparedStatement预编译还是会存在SQL注入风险的。

PreparedStatement是继承Statement的子接口。

PreparedStatement会对SQL语句进行预编译,不论输入什么,经过预编译后全都以字符串来执行SQL语句。

PreparedStatement会先使用?作为占位符将SQL语句进行预编译,确定语句结构,再传入参数进行执行查询。如下述代码:

PreparedStatement是继承Statement的子接口。
PreparedStatement`会对SQL语句进行预编译,不论输入什么,经过预编译后全都以字符串来执行SQL语句。
PreparedStatement会先使用`?`作为占位符将SQL语句进行预编译,确定语句结构,再传入参数进行执行查询。如下述代码:

首先讲解正确的预编译,示例代码如下:

这里就不存在sql注入了(还未有绕过方法)

访问目标地址,发现数据无法进行注入

但是存在一种情况,就是虽然使用的是预编译,但是还是进行的拼接进行查询。由于开发人员疏忽或经验不足等原因,虽然使用了预编译PreparedStatement,但没有根据标准流程对参数进行标记,依旧使用了动态拼接SQL语句的方式,进而造成SQL注入漏洞。

示例代码如下

重点关注下述代码:

String sql = "select * from users where username = '" + username + "'";
PreparedStatement preparestatement = conn.prepareStatement(sql);
ResultSet rs = preparestatement.executeQuery();

可以看到这里用的拼接,所以存在漏洞。

' union select 1,2,version()'

Order by注入

在SQL语句中,order by语句用于对结果集进行排序。order by语句后面需要是字段名或者字段位置。在使用PreparedStatement预编译时,会将传递任意参数使用单引号包裹进而变为了字符串。如果使用预编译方式执行order by语句,设置的字段名会被数据库认为是字符串,而不在是字段名。因此,在使用order by时,就不能使用PreparedStatement预编译了。

示例代码如下:

这里我们使用时延注入,发现有明显的时间延迟。

Mybatis中的SQL注入

在Mybatis中拼接SQL语句有两种方式:一种是占位符#{},另一种是拼接符${}。

占位符#{}:对传入的参数进行预编译转义处理。类似 JDBC 中的PreparedStatement。

比如:select * from user where id = #{number},如果传入数值为1,最终会被解析成select * from user where id = "1"。

拼接符${}:对传入的参数不做处理,直接拼接,进而会造成SQL注入漏洞。

比如:比如:select * from user where id = ${number},如果传入数值为1,最终会被解析成select * from user where id = 1。

#{}可以有效防止SQL注入漏洞。${}则无法防止SQL注入漏洞。

因此在我们对JavaWeb整合Mybatis系统进行代码审计时,应着重审计SQL语句拼接的地方。除非开发人员的粗心对拼接语句使用了${}方式造成的SQL注入漏洞。在Mybatis中有几种场景是不能使用预编译方式的,比如:order by、in,like。

漏洞案例

示例代码如下:

该网站SQL是Mabatis类型的,sql语句一般写在Mapper中

通过关键词进行全局搜索,找到Dao层,以及Servers层

跟进代码

找到相关接口以及参数

直接拿到sqlmap中跑

python sqlmap.py -u http://127.0.0.1:7089/sqli/mybatis/orderby?sort=id --batch

漏洞出发点找到

修复方案

in注入

正确的做法是需要使用foreach配合占位符#{}实现IN查询。比如:

<!-- where in 查询场景 -->
<select id="select" parameterType="java.util.List" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name IN
    <foreach collection="names" item="name" open="(" close=")" separator=",">
      #{name}
    </foreach>
</select>
like注入

下面代码是正确的做法,可以防止SQL注入漏洞,如下。

SELECT * FROM users WHERE name like CONCAT("%", #{name}, "%")
表,字段名称

(Select, Order by, Group by 等)

// 插入数据用户可控时,应使用白名单处理
// example for order by

String orderBy = "{user input}";
String orderByField;
switch (orderBy) {
    case "name":
        orderByField = "name";break;
    case "age":
        orderByField = "age"; break;
    default:
        orderByField = "id";
}
JDBC
String name = "foo";

// 一般查询场景
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement pre = conn.prepareStatement(sql);
pre.setString(1, name);
ResultSet rs = pre.executeQuery();

// like 模糊查询场景
String sql = "SELECT * FROM users WHERE name like ?";
PreparedStatement pre = conn.prepareStatement(sql);
pre.setString(1, "%"+name+"%");
ResultSet rs = pre.executeQuery();

// where in 查询场景
String sql = "select * from user where id in (";
Integer[] ids = new Integer[]{1,2,3};

StringBuilder placeholderSql = new StringBuilder(sql);
for(int i=0,size=ids.length;i<size;i++) {
    placeholderSql.append("?");
    if (i != size-1) {
        placeholderSql.append(",");
    }
}
placeholderSql.append(")");

PreparedStatement pre = conn.prepareStatement(placeholderSql.toString());
for(int i=0,size=ids.length;i<size;i++) {
    pre.setInt(i+1, ids[i]);
}
ResultSet rs = pre.executeQuery();
Spring-JDBC
JdbcTemplate jdbcTemplate = new JdbcTemplate(app.dataSource());

// 一般查询场景
String sql = "select * from user where id = ?";
Integer id = 1;
UserDO user = jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(UserDO.class), id);

// like 模糊查询场景
String sql = "select * from user where name like ?";
String like_name = "%" + "foo" + "%";
UserDO user = jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(UserDO.class), like_name);

// where in 查询场景
NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(app.dataSource());

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("names", Arrays.asList("foo", "bar"));

String sql = "select * from user where name in (:names)";
List<UserDO> users = namedJdbcTemplate.query(sql, parameters, BeanPropertyRowMapper.newInstance(UserDO.class));
Mybatis XML Mapper
<!-- 一般查询场景 -->
<select id="select" parameterType="java.lang.String" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name = #{name}
</select>

<!-- like 查询场景 -->
<select id="select" parameterType="java.lang.String" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name like CONCAT("%", #{name}, "%")
</select>

<!-- where in 查询场景 -->
<select id="select" parameterType="java.util.List" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name IN
    <foreach collection="names" item="name" open="(" close=")" separator=",">
      #{name}
    </foreach>
</select>
Mybatis Criteria
public class UserDO {
    private Integer id;
    private String name;
    private Integer age;
}

public class UserDOExample {
    // auto generate by Mybatis
}

UserDOMapper userMapper = session.getMapper(UserDOMapper.class);
UserDOExample userExample = new UserDOExample();
UserDOExample.Criteria criteria = userExample.createCriteria();

// 一般查询场景
criteria.andNameEqualTo("foo");

// like 模糊查询场景
criteria.andNameLike("%foo%");

// where in 查询场景
criteria.andIdIn(Arrays.asList(1,2));

List<UserDO> users = userMapper.selectByExample(userExample);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

containerd: failed to load TOML: /etc/containerd/config.toml

1、启动containerd报错截图如下: 2、解决办法: 2.1 停止containerd服务 systemctl stop containerd 2.2 删除containerd文件 rm -rf /var/lib/containerd/* /var/lib/containers/* 132 yum install docker-ce-20.10.* docker-ce-cli-20.10.* containerd.io -y 133 …

Visual Studio扩展开发

对于Roslyn(编译平台)的扩展 内容来源:https://learn.microsoft.com/zh-cn/dotnet/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix 创建项目 解决方案项目介绍 Resources.resx介绍 填入的内容会在错误列表中显示

高考填报志愿选专业,理科生如何选专业?

理科相对比较好选专业&#xff0c;因为院校多&#xff0c;专业多&#xff0c;当然招生名额也多&#xff0c;对于一般普通家庭的学生来说&#xff0c;理科生比文科生的就业前景好。但这是一个就业形势十分严峻的时代&#xff0c;没有谁敢绝对的说自己一定能在某个专业中学到知识…

计算机 SCI期刊,中科院1区TOP,IF=10+,老牌期刊,声誉和口碑上佳!

一、期刊名称 IEEE Transactions on Wireless Communications 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;10.4 中科院分区&#xff1a;1区TOP 三、期刊征稿范围 《IEEE Transactions on Wireless Communications》出…

【AI大模型】Transformers大模型库(六):torch.cuda.OutOfMemoryError: CUDA out of memory解决

​​​​​​​ 目录 一、引言 二、CUDA显存超出&#xff08;CUDA out of memory&#xff09; 2.1 概述 2.2 解决方案 2.3 代码示例 2.4 查看显存 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库&#xff0c;为huggingface上数以万计的预训练大…

java实战——图书管理项目

文章目录 项目所需要的技术栈项目演示项目准备工作环境准备数据库数据准备 前后端交互分析&#xff08;前端代码我们使用现成&#xff09;图书列表界面的创建查看前端发送的请求根据前端接收的返回值来编写model层根据请求编写controller层根据controller编写Service根据Servic…

初识PHP

一、格式 每行以分号结尾 <?phpecho hello; ?>二、echo函数和print函数 作用&#xff1a;两个函数都是输出内容到页面中&#xff0c;多用于代码调试。 <?php echo "<h1 styletext-align: center;>test</h1>"; print "<h1 stylet…

【linux网络(二)】网络基础之套接字编程

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. 端口号详…

vs、utf-8、utf-8bom乱码分析及实测

1、系统默认控制台命令行编码 windows命令行默认的编码是ANSI&#xff0c;中文系统下则就是GBK&#xff0c;GBK是对GB2312编码的扩展兼容GB2312&#xff0c;可以等同理解为就是GB2312。 2、vs2022默认新建项目编码 vs默认项目文件编码格式为UTF-8 BOM 默认字符集 Unicode 最终…

MNIST数据集导出

MNIST数据集导出 文章目录 MNIST数据集导出1、 MNIST数据集介绍2、 MNIST数据集下载2.1 使用Pytorch自带的MNIST数据集 3、 MNIST数据集解析3.1 训练集图片文件解析规则3.2 训练集标签文件解析规则3.3 测试集图片文件解析规则3.4 测试集标签文件解析规则 4、 MNIST数据集转图片…

动手学深度学习31 深度学习硬件 CPU和GPU

动手学深度学习31 深度学习硬件 CPU和GPU CPU和GPU主频 QA PPT&#xff1a; https://courses.d2l.ai/zh-v2/assets/pdfs/part-2_1.pdf 视频&#xff1a; https://www.bilibili.com/video/BV1TU4y1j7Wd/?p2&spm_id_frompageDriver&vd_sourceeb04c9a33e87ceba9c9a2e5f09…

nginx ws长连接配置

nginx ws长连接配置 http根节点下配上 map $http_upgrade $connection_upgrade {default upgrade; close;}如下&#xff1a; server服务节点下&#xff0c;后端接口的代理配置 proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connec…

2. 面向对象编程推导

1. 面向过程编程 面向过程编程(Procedure-Oriented Programming, POP): 是一种关注于解决问题步骤或过程的编程范式.面向过程编程核心思想: 将复杂问题分解为一系列简单, 可执行的步骤(即过程或函数), 并按照特定的顺序依次执行这些步骤, 直到问题得到解决. 每个步骤(过程或函…

compose for desktop

then 叠加修饰符功能的作用 val reusableModifier Modifier.fillMaxWidth().background(Color.Red).padding(12.dp)// Append to your reusableModifier reusableModifier.clickable { /*...*/ }// Append your reusableModifier otherModifier.then(reusableModifier)https:…

springboot物流管理系统-计算机毕业设计源码00781

摘要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对物流管理系统等问题&#xff0c;对如何通过计…

时间处理获取交易日(考虑兼容性问题)

在获取交易日时间的处理上&#xff0c;出现了苹果14不兼容的问题&#xff0c;就这个问题记录下。 一、获取交易日的代码 封装了一个js文件&#xff0c;在untils目录下&#xff0c;先看代码&#xff0c;然后我讲下思路。 // 获取节假日数据 import { getCalendarHolidays } …

使用脚手架创建vue2项目(关闭eslint语法检查 、运行项目时自动打开网址、src文件夹简写方法)

使用脚手架创建vue2项目会默认安装的插件&#xff08;eslint) 这个插件是检查语法的。 假设我们在main.js中定义了一个变量&#xff0c;没有使用 eslint 就会检测出错误 &#xff08;事实是我们并没有写错而是eslint 给我们判断是错的&#xff0c;所以这样会很麻烦&#xff…

怎么监视员工电脑屏幕?电脑监控软件监控屏幕的六个步骤

监视员工电脑屏幕通常涉及使用专门的电脑监控软件&#xff0c;这些软件设计用于帮助企业管理人员合法合规地监督员工的工作状态、提高工作效率并确保信息安全。以下是实施员工电脑屏幕监视的一般步骤和注意事项&#xff1a; 1. 选择合适的监控软件 首先&#xff0c;选择一款适…

Idea多线程调试

在 IntelliJ IDEA 中调试多线程应用程序可能会有些复杂&#xff0c;因为多个线程可能会同时运行和交互。不过&#xff0c;IDEA 提供了一些强大的工具来帮助你进行多线程调试。以下是一些关键步骤和技巧&#xff0c;帮助你有效地调试多线程应用程序&#xff1a; 创建一个示例多线…

查分易成绩查询入口

今天我来分享一个老师超实用的小技巧&#xff0c;那就是如何用查分易来打造一个专属的成绩查询入口哦&#xff01;无论是我们勤奋的学生们&#xff0c;还是关心孩子学习的家长们&#xff0c;都可以轻松查到自己的成绩信息。来来来&#xff0c;让我来一步步教你怎么用查分易搞定…