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

前言

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);

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

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

相关文章

Stack详解(含动画演示)

目录 Stack详解1、栈数据结构动画演示2、Stack的继承体系3、Stack的push (入栈)方法4、Stack的pop (出栈)方法5、Stack是如何利用Vector实现栈数据结构的&#xff1f;6、自己实现栈(不借助JDK提供的集合)7、自己实现栈(借助JDK提供的集合)利用 ArrayDeque 实现高性能的非线程安…

SyntaxError: Non-UTF-8 code starting with ‘\xbd‘ in file错误解决

在运用python的pandas和numpy的内容环境下&#xff0c;运行代码时发生以下错误&#xff1a; C:\ProgramData\Anaconda3\python.exe D:/zafile/py数据分析与应用/数据分析代码/14.2、紧急电话数据分析.pyFile "D:/zafile/py数据分析与应用/数据分析代码/14.2、紧急电话数据…

7.枚举和模式匹配

一、enum枚举 1.1 定义枚举类型和对应的数据 //定义枚举 #[derive(Debug)] enum IpAddrKind{IPv4,IPv6, }struct Ipaddr{kind: IpAddrKind, //设置kind为IpAddrKind的枚举类型address: String, }fn route(ip_addr: &Ipaddr){println!("ip_type {:#?}", ip_a…

如何基于Nginx配置代理服务器实现邮件告警

当代企业信息化系统建设中&#xff0c;将内网与公网进行隔离是一种非常常见的措施&#xff0c;它可以有效保护企业内部数据不被外泄&#xff0c;有助于企业构建一个更加安全的网络环境&#xff0c;保护企业资产和用户隐私。但另一方面&#xff0c;内网与公网隔离也会带来一些问…

【STM32】输入捕获应用-测量脉宽或者频率(方法2)

链接&#xff1a;https://blog.csdn.net/gy3509/article/details/139629893?spm1001.2014.3001.5502&#xff0c;讲述了只使用一个捕获寄存器测量脉宽和频率的方法&#xff0c;其实测量脉宽和频率还有一个更简单的方法就是使用PWM输入模式&#xff0c;PWM输入模式需要占用两个…

推挽式B类功率放大器的基本原理

单晶体管 B 类放大器&#xff08;图 1&#xff09;使用高 Q 值储能电路作为负载来抑制高次谐波分量。通过采用高 Q 谐振电路&#xff0c;输出电压仅包含基波分量&#xff0c;使放大器能够忠实地再现输入信号。尽管集电极电流是半波整流正弦波&#xff0c;但高 Q 值储能电路将谐…

Python基础教程(十):装饰器

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

Non-aligned Supervision for Real Image Dehazing

原文链接&#xff1a;https://www.semanticscholar.org/paper/Non-aligned-supervision-for-Real-Image-Dehazing-Fan-Guo/7595d39e71ae58343e8728fc1af0e18ffe38218b 数据集&#xff1a;https://www.cityscapes-dataset.com/ 真实的图像去雾的非对准监督 摘要 由于天气条件…

淘宝评论数据信息接口

淘宝评论API接口是一种用于获取淘宝用户评论信息的接口&#xff0c;联讯数据可以帮助商家和消费者获取到商品的评价信息&#xff0c;以便更好地了解商品的质量和用户体验。以下是关于淘宝评论API接口的一些内容&#xff1a; 一、接口功能 淘宝评论API接口提供了商品评价信息的…

doc 和 docx 文件的区别

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

第32章-SDN概述

1. SDN的由来 2. SDN的基本概念 1. SDN的由来 计算机产业的发展&#xff1a; 从 1946 年第一代电子管计算机面世到如今&#xff0c;计算机的形态也发生了翻天覆地的变化。从大型机到个人 PC&#xff0c;计算机在不断地推陈出新&#xff0c;且创新发展的势头越加猛烈。究其原因…

信号处理中的相位

相位 用来描述波动或振动状态。 在信号处理和通信领域&#xff0c;相位通常指的是信号相对于某一参考信号的延迟。 在周期性信号中&#xff0c;相位通常以角度&#xff08;弧度或度&#xff09;来表示&#xff0c;表示信号的周期性变化相对于参考信号的位置。 在非周期性信号中…

教你一招,告警恢复时如何拿到恢复时的值?

Prometheus 生态的原生做法&#xff0c;由于阈值是放在 promql 中的&#xff0c;恢复时的消息中难以拿到恢复时的值&#xff0c;夜莺 v7.0.0.beta10 版本开始&#xff0c;提供了一种较为简单的内置方式&#xff0c;解决这个问题。下面我们就来看一下如何实现这个能力。 升级方…

医疗设备维修培训服务的安全性和可靠性

当前&#xff0c;医疗设备维修服务行业市场已经形成了一个庞大的产业链&#xff0c;涵盖了设备检测、故障诊断、维修维护等多个环节。随着医疗设备的日益复杂和高端&#xff0c;对专业维修服务的需求也在不断增加。因此&#xff0c;市场上涌现出了一批专业的医疗设备维修服务提…

Anaconda3 下载安装卸载

1、下载 官网链接&#xff1a;Download Now | Anaconda Step1&#xff1a;进入官网 Anaconda | The Operating System for AI Step2&#xff1a;进入下载页面&#xff0c;选择要Anaconda软件安装包 2、安装 Step1: 点击 Anaconda3-2024.02-1-Windows-x86_64.exe 安装包进行安…

Windows 11中查找和删除旧文件的几种方法,总有一种适合你

序言 如果你的电脑存储空间不足,最好的办法就是找到并删除旧的、不需要的文件。Windows 11提供了多种方法来查找这些占用存储空间的项目,我们将在本指南中向你展示这些方法以及如何使用它们。 使用存储感知 存储感知是Windows 11的内置功能,可帮助自动清理旧文件。你可以…

适合营销的叙事可视化

背景 数据可视化与数据故事化的差异和相似点&#xff0c;以及它们如何协同工作&#xff0c;将你的数据转化为清晰、简洁、可操作的信息&#xff0c;以便您的组织使用。 什么是数据可视化&#xff1f; 数据可视化通过图像传达信息——这是你所收集数据的视觉表示。通过提供原…

(六)React组件通信

理解组件通信 概念&#xff1a;组件通信就是组件之间的数据传递&#xff0c;根据组件嵌套关系不同&#xff0c;有不同的通信方式。 A - B 父子通信B - C 兄弟通信A - E 跨层通信 1. 父传子 – 基础实现 实现步骤&#xff1a; 父组件传递数据 - 在子组件标签上绑定属性子组…

eNSP学习——配置高级的访问控制列表

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建OSPF网络 3、配置Telnet 4、配置高级ACL控制访问 需要eNSP各种配置命令的点击链接自取&#xff1a;华为&#xff45;NSP各种设备配置命令大全PDF版_ensp配置命令大全资源-…

使用Stream实现Web应用,使用YOLOv8模型对图像进行目标检测为例。

Streamlit是一个开源的Python框架&#xff0c;专门设计用于快速构建和共享数据应用程序。它使数据科学家和机器学习工程师能够通过编写简单的Python脚本&#xff0c;轻松创建美观、功能强大的Web应用程序&#xff0c;而无需具备前端开发的经验。 其他框架或web应用可以看下面两…