【JAVA代码审计】从零开始的JDBC下的SQL注入审计

2022-11-11 15:33:45 浏览数 (1)

Hello,各位小伙伴大家好~

这里是你们的小编Monster .

今天起开始更新JAVA代码审计相关内容了~

首先从大家最熟悉的SQL注入讲起

包含以下内容:

(1)JDBC下的JAVA代码审计

(2)Mybatis下的JAVA代码审计

(3)Hibernate下的JAVA代码审计

因为是从零开始的代码审计分享

所以本套分享会从环境搭建开始讲起~

今天的内容是JDBC下的JAVA代码审计,

一起来看看吧,Here We Go!

Part.1

JDBC初探

什么是JDBC?

想要学习SQL注入,就需要从最简单的JDBC看起,什么是JDBC呢?

JDBC是JAVA访问各种不同数据库的统一标准规范,该规范用于定义接口,具体的实现由各大数据库厂商各自实现。

因此我们只需要会调用JDBC接口中的方法即可,不用关注背后的类是怎么实现的,由数据库厂商提供数据库驱动,从用户侧大大简化了数据库的配置难度。

JDBC的核心API如下所示:

主要通过两种方法执行SQL语句,分别是:

Statement

PrepareStatement

因此我们审计JDBC下的SQL注入,就可以从以上两个函数入手。

JDBC环境搭建

既然是手把手教学,那么我们就从零开始写一套JDBC代码熟悉一下。

//本文使用的所有源码都可以从文末获取!!

我们来写一个用户登录的简单场景。

首先通过DBeaver、Navicat等工具连接本地数据库,并创建多个账号用于登录:

接着我们来看看如何创建一个java web项目。

打开idea,新建一个项目:

//这里通过tomcat8.5版本启动,jdk使用1.8版本:

选择web服务,点击Finash完成创建:

创建完成后,查看Project Settings可以看到项目的资源配置情况:

//例如源码为src/main/java文件夹

接下来开始配置jdbc,创建一个lib文件用于放mysql数据库驱动:

右键Add as library,进行调用:

创建类user,变量与数据库中user表对应:

//get、set方法用于调用、修改变量值

创建类demo01,配置数据库连接:

//除此之外还有c3p0,druid等多种配置方式,功能都是一样的,代码审计无需过多关注。

右键运行demo01,因为我密码错误,这里出现报错:

修改password为正确口令后,顺利连接上数据库:

添加用于输入用户名、口令的代码:

接下来,配置statement用于执行sql语句:

//结合if语句,查询并判断用户名、口令是否正确

查询完毕后还需要释放掉statement连接:

尝试查询一下,输入正确用户名、口令:

输入错误的用户名、口令:

环境搭建完毕~

Part.2

JDBC注入审计

Statement拼接不当

上面的环境就是采用的Statement方式进行SQL查询:

可控点为name和password字段:

代码语言:javascript复制
select * from user where name='name' and passwd=’password’;

如果输入:

代码语言:javascript复制
name=admin‘ #
password=123123

则可通过sql注入绕过登录密码:

因为此时的查询语句变成了:

代码语言:javascript复制
select * from user where name='name' #' and passwd=’123123’

第一个单引号让#号键发生了逃逸,并注释了后面的密码查询字段,实际执行的语句是:

代码语言:javascript复制
select * from user where name='name‘

可以看出Statement方法是不安全的。

PrepareStatement拼接不当

PrepareStatement 是 Statement 接口的子接口,继承父接口中的所有方法,并且它是一个预编译的 SQL 语句。

相较于Statement,PrepareStatement有两个优势:

(1)因为有预先编译的功能,提高 SQL 的执行效率。

(2)预编译可以有效的防止 SQL 注入的问题,安全性更高。

理论上PrepareStatement安全性更好,但如果误用也会导致SQL注入存在。

修改上述代码为:

这里采用prepareStatement方式执行sql语句,但依然存在注入:

原因在于PrepareStatement方法需要使用“?”对变量位进行占位才会进行预编译。

修改代码为:

此时再尝试sql注入,已经失败了:

order by等特殊情况

那是不是我们使用预编译的方式,就可以避免sql注入了?

答案是否定的,因为有些情况是不适用预编译的。

预编译在对?传参过程中,会给所传的参数前后加上单引号。

首先将数据库中id乱序:

通过预编译的方式,使用id进行查询,没能得到正确的结果:

order by失效,因为此时执行的sql语句为:

代码语言:javascript复制
select * from user order by ‘id’

不通过预编译传参的方式进行查询,order by生效:

此时执行的sql语句为:

代码语言:javascript复制
select * from user order by id

但不使用预编译,如果参数可控又可能存在sql注入,这种情况只能配合关键字黑白名单过滤的方式进行防护了。

Part.3

结语

以上就是今天的全部内容啦~

0 人点赞