java之sql注入审计

1 基础环境搭建

1.1 mysql数据库搭建

phpStudy是一个PHP调试环境的程序集成包,PHP+Mysql+Apache。 通过phpstduy下载与安装指

定版本的mysql数据库【可以同时下载多个版本,便于应对不对的系统及复现漏洞便捷切换多个版本】

完成下载后,启动服务即可。

(2)使用navicat连接mysql服务后可进行相关操作,连接成功后,打开数据库,可以打开数据表直接读写,也可以执行相关SQL命令

1.2 mysql数据库配置

MySQL 安装包:MySQL :: Download MySQL Community Server

MySQL安装教程:MySQL 安装 | 菜鸟教程

MySQL配置文件


允许远程连接

mysql>GRANT ALL PRIVILEGES ON *.* TO ‘root’@‘%’ IDENTIFIED BY‘密码’ WITH GRANT OPTION;
mysql>flush privileges

MySQL开启日志记录

找到MySQL的配置文件 my.ini,在 [mysqld] 中port参数下方添加一行记录:

log="D:/phpstudy_pro/Extensions/MySQL5.1.60/data" 保存重启数据库 此时会在指

定路径下新增一个日志文件。

2 在线SQL练习平台

2.1 SQL Fiddle

SQL Fiddle - Online SQL Compiler for learning & practice, SQL Fiddle 支持 MySQL、SQL Server、SQLite 等主流的 SQL 引擎,在这

里可以选择练习的数 据库以及版本号 。

2.2 SQLZOO

https://sqlzoo.net/, SQLZOO包括了 SQL 学习的教程和参考资料, 支持 SQL Sever、Oracle、MySQL、DB2、 PostgreSQL等多个 SQL 搜索引擎 。

2.3 SQL Bolt

SQLBolt - Learn SQL - Introduction to SQL, SQLBolt 是一个适合小白学习 SQL 的网站,这里由浅及深的介绍了 SQL 的知识,每一个章节是一组相关的 SQL 知识点,且配备着相应的练习 。

3 SQL语法学习

SQL (Structured Query Language:结构化查询语言) 用于管理关系数据库管理系统 (RDBMS),可以访问和处理数据库,包括数据插入、查询、更新和删除。

• 提示:

SQL 对大小写不敏感

分号:分号是在数据库系统中分隔每条 SQL 语句的标准方法,这样就可以在对服务器的相同 请求中执行一条以上的语句

3.1 Select查询

SELECT 语句用于从数据库中选取数据。

语法格式: SELECT column_name,column_name FROM table_name; 或 SELECT * FROM table_name

3.2 WHERE语句

WHERE 子句用于过滤记录.

语法格式: SELECT column_name,column_name FROM table_name WHERE column_name operator value;

例如:

where之=查询数据:select * from javacode_tb1 where audit_id = 1;

3.3 UNION联合查询

UNION 用于合并两个或多个 SELECT 语句的结果集,并消去表中任何重复行。 UNION 内部的

SELECT 语句必须拥有相同数量的列,列也必须拥有相似的数据类型。 同时,每条 SELECT 语句中的

列的顺序必须相同。

语法格式: SELECT column_name FROM table1 UNION SELECT column_name FROM table2

union查询:select audit_author from javacode_tb1 union select audit_author from
javacode_tb2 order by audit_author;

提示:默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL

3.4 ORDER BY 关键字

ORDER BY 关键字用于对结果集进行排序。 默认按照升序对记录进行排序。如果需要按照降序对记录

进行排序,您可以使用 DESC 关键字。

语法结构: SELECT column_name,column_name FROM table_name ORDER BY

column_name,column_name ASC|DESC

例如: 按照 “alexa” 列升序排序: SELECT * FROM Websites ORDER BY alexa; 按照 “alexa”

列降序排序: SELECT * FROM Websites ORDER BY alexa DESC;

(1)order by 后面可以跟字段名,表达式和字段的位置号

例如: select * from student_table order by student_age

例如: select * from student_table order by 3;

思考:order by 后的索引是从0开始还是从1开始?经过测试mysql数据库的order by 索引从1开始,而非0

当表达式不成立时,执行后者,成立执行前者

提示:if语句返回的是字符类型,不是整型

select * from student_table order by if(1=1,student_age,student_tele)

3.5 like模糊查询

LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。 "%" 符号用于在模式的前后定义通配符(默认字母)。

基本语法: SELECT column_name(s) FROM table_name WHERE column_name LIKE pattern

例如:select * from javacode_tb1 where audit_author like '%s%';

3.6 in关键字

IN 操作符通常用来在 WHERE 子句中规定多个值【而不是一个范围】

语法格式: SELECT column_name(s) FROM table_name WHERE column_name IN

(value1,value2,...) ;

例如: 选取 student_name 为 "tom" 或 ”jerry" 的所有网站,

SELECT * FROM student_table WHERE student_age IN (17,14);

3.6 group by 分组查询

GROUP BY 语句通常结合一些聚合函数来使用,根据一个或多个列对结果集进行分组。

常用聚合函数包括:count() —— 计数 、sum() —— 求和 、avg() —— 平均数 、max() —— 最大值 、min() —— 最小值

例如1:

(1)创建数据表:

DROP TABLE IF EXISTS employee_tbl;  
CREATE TABLE employee_tbl (  id INT(11) NOT NULL,  name VARCHAR(100) NOT NULL DEFAULT '',   date DATETIME NOT NULL,  login_count TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '登录次数', PRIMARY KEY (id)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(2)向数据表中插入数据:

INSERT INTO employee_tbl VALUES ('1','小明', '2016-04-22 15:25:33', '1'), ('2', '小王', '2016-04-20 15:25:47', '3'), ('3', '小丽', '2016-04-19 15:26:02', '2'), ('4', '小王', '2016-04-07 15:26:14', '4'), ('5', '小明', '2016-04-11 15:26:40', '4'), ('6','小明', '2016-04-04 15:26:54', '2');

(3)执行group by查询操作:

SELECT name, COUNT(*) FROM employee_tbl GROUP BY name;

3.8. 数据库常见函数

3.8.1 聚焦函数

  • avg:返回指定字段的数据的平均值

  • count:返回指定字段的数据的行数(记录的数量)

  • max:返回指定字段的数据的最大值

  • min:返回指定字段的数据的最小值

  • sum:返回指定字段的数据之和

3.8.2 处理字符串函数

  • 合并字符串函数:concat(str1,str2,str3…)

  • 比较字符串大小函数:strcmp(str1,str2)

  • 获取字符串字节数函数:length(str)

  • 获取字符串字符数函数:char_length(str)

  • 字母大小写转换函数:大写:upper(x),ucase(x);小写lower(x),lcase(x)

  • 字符串查找函数:find_in_set(str1,str2)、field(str,str1,str2,str3…)、locate(str1,str2)、position(str1 INstr2)、nstr(str1,str2)

  • 获取指定位置的子串:elt(index,str1,str2,str3…)、left(str,n)、right(str,n)、substring(str,index,len)

  • 字符串去空函数:ltrim(str)、rtrim(str)、trim()

  • 字符串替换函数:insert(str1,index,len,str2)、replace(str,str1,str2)

3.8.3 处理时间日期的函数

  • 获取当前日期:curdate(),current_date()

  • 获取当前时间:curtime(),current_time()

  • 获取当前日期时间:now()

  • 从日期中选择出月份数:month(date),monthname(date)

  • 从日期中选择出周数:week(date)

  • 从日期中选择出年数:year(date)

  • 从时间中选择出小时数:hour(time)

  • 从时间中选择出分钟数:minute(time)

  • 从时间中选择出今天是周几:weekday(date),dayname(date)

3.8.4 处理数值的函数

  • 绝对值函数:abs(x)

  • 向上取整函数:ceil(x)

  • 向下取整函数:floor(x)

  • 取模函数:mod(x,y)

  • 随机数函数:rand()

  • 四舍五入函数:round(x,y)

  • 数值截取函数:truncate(x,y)

3.8.5 与安全相关的函数应用(重要)

  • 获取当前用户:(1)select user();(2)select current_user();(3)select current_user;(4)selectsystem_user();(5)select session_user();

  • 获取当前数据库:(1)select database();(2)select schema();

  • 获取所有数据库:SELECT schema_name FROM information_schema.schemata;

  • 元数据:人家Mysql自有的数据库中数据库所存储的数据

  • 获取服务器主机名:select @@HOSTNAME;

  • 查询用户是否有读写权限:SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%username%';SELECT file_priv FROM mysql.user WHERE user = 'root';(需要root用户来执行)

  • 注释:(1)单行注释:--(2)多行注释://

  • 字符串截取:SELECT substr('abc',2,1);

  • MySQL特有的写法:/! SQL语句 / :这种格式里面的SQL语句可以被当成正常的语句执行。当版本号大于!后面的一串数据,SQL语句则执行

  • 空格被过滤编码绕过:%20, %09, %0a, %0b, %0c, %0d, %a0%a0UNION%a0select%a0NULL

  • or,and绕过:or可以使用||代替,and可以使用&&代替。

  • union select被过滤:(1)union(select(username)from(admin)); :union和select之间用(代替空格。

  • select 1 union all select username from admin; :union和select之间用all,还可以用distinct3

  • select 1 union%a0select username from admin; :同样的道理%a0代替了空格。select 1 union/!select/username from admin; 。

  • select 1 union/hello/username from admin; 注释代替空格

  • 关键字where被过滤使用limit来代替(limit被用于强制 SELECT 语句返回指定的记录数)

  • limit绕过:使用having函数(having:在聚合后对组记录进行筛选,类似于where作用,经常与grop by使用)

  • having函数绕过:group_concat():将相同的行组合起来

4 SQL注入漏洞

在输入的字符串中注入 SQL 语句,如果应用相信用户的输入而对输入的字符串没进 行任何的过滤处理,那么这些注入进去的 SQL 语句就会被数据库误认为是正常的 SQL 语句而 被执行。

4.1 万能密码绕过登陆

select * from admin where username='$username'and password='$password' 若输入 ' or 1=1-- (后面有个空格)

则后端变成: select * from admin where username='' or 1=1 -- 'and password='$password’

例如:

打开SQL注入漏洞测试(登录绕过)_SQL注入_在线靶场_墨者学院_专注于网络安全人才培养,

输入 ' or 1=1# 或'or 1=1--

4.2 SQL注入分类

  • 有无回显:显注、盲注

  • 按数据库类型:MySQL、Access、SQL Server、Oracle等

  • 注入点参数类型:数字型、字符型 当发生注入点的参数为整数时,比如 ID,num,page等,这种形式的就属于数字型注入漏洞。 当注入点是字符串时,则称为字符型注入,字符型注入需要引号来闭合。

  • 按请求方式:GET注入、POST登录框注入、Cookie验证注入、HTTP头注入

4.3 常见的注入点

应用程序和数据交互的地方:

  • Authentication(认证页面)

  • Search Fields (搜索页面)• Post Fields (Post请求)

  • Get Fields (Get请求)

  • HTTP Header(HTTP头部)

  • Cookie

4.4 数据库特性

(1) 数据库特定表

  • and exists(select count(*) from msysobjects)

mysysobjects表为access特有,一般返回权限不足

  • and exists(select count(*) from sysobjects)

sysobjects表为SqlServer特有,一般返回正常

  • and (select count(*) from information_schema.TABLES)>0

information_schema为MySQL 5.0 及以上版本特有

(2) 数据库报错

数据库错误代码错误描述示例
MySQL1064SQL语法错误ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '...' at line X
OracleORA-00933SQL命令未正确结束ORA-00933: SQL command not properly ended
OracleORA-00904无效的标识符ORA-00904: "COLUMN_NAME": invalid identifier
SQL ServerMsg 102语法错误Msg 102, Level 15, State 1, Line X Incorrect syntax near '...'.
PostgreSQLERROR语法错误ERROR: syntax error at or near "..." LINE X: ...

(3) 注释符

以下是一个表格,展示了各个数据库系统中常用的注释符:

数据库单行注释符多行注释符
MySQL#--(注意--后面有一个空格)/* ... */
Oracle--/* ... */
SQL Server--/* ... */
PostgreSQL--/* ... */
Access无直接注释符,通常在代码编辑器中使用注释功能无直接注释符

4.5 数据库特性

  • information_schema.schemata 存放所有数据库名,schema_name为数据库名字段

  • information_schema.tables 存放所有数据库的表名,table_schema存放数据库名,table_name字段存放表名

  • information_schema.columns 存放所有数据库表的所有列名,table_schema 库名,table_name表名,column_name列

4.6 SQL注入常规思路(MySQL)

  • 判断注入点,检测网站脆弱性

  • 判断数据库类型和版本 借助数据库特性进行判断,如数据库支持的函数、符号等

  • order by 判断列数

  • 找到可以显示数据的位置

  • 爆库名

  • 爆表名

  • 显示admin表中的字段名

  • 显示字段内容

4.7 sql注入靶场

  • SQL注入在线靶场:RedTiger's Hackit

  • DVWA:https://dvwa.co.uk/

  • sqli-labs源码:https://github.com/Audi-1/sqli-labs

5 SQL注入漏洞审计

执行对象是SQL的执行者。 目前常用的执行对象接口有三种: Statement、PreparedStatement

CallableStatement 。

5.1 JDBC

5.1.1 Statement

Statement 主要用于执行静态SQL语句,即内容固定不变的SQL语句。Statement 每执行一次都要对传入的SQL语句编译一次,效率较低 。

例如:

String name = "tom";
String sqlString = "select * from student_table where student_name = '" + name + "'";
Connection conn = open();
ResultSet rs = null;
Statement stmt = conn.createStatement();
rs = stmt.executeQuery(sqlString);
System.out.println(stmt);
while (rs.next()){
System.out.println("student_id:" + rs.getInt("student_id") + "\t"
+ "student_name:" + rs.getString("student_name") + "\t"
+ "student_age:" + rs.getString("student_age") + "\t"
+ "student_tele:" + rs.getString("student_tele") + "\t");}
}
​

若 name 为用户可控参数,当用户传入 ' or 1 = 1 # 则sqlString为:select * from student_table where student_name = ' ' or 1 = 1 # ', 可以看到程序输出 student_table 表中所有条目.

5.1.2 PreparedStatement

PreparedStatement是预编译参数化查询执行SQL语句的方式。

例如

String sql = "select * from student_table where name = ?";
Connection conn = open();
PreparedStatement pstmt = (PreparedStatement) conn.prepareStatement(sql); //对占位符进行初始化
pstmt.setString(1, "tom"); pstmt.executeQuery();

看到使用PreparedStatement之后,特殊符号被转义,无法按设想的SQL语句执行了。


看到使用PreparedStatement之后,特殊符号被转义,无法按设想的SQL语句执行了。

早期开发为了满足需求,不得已的情况直接使用普通对象进行了变量的拼接执行了sql

5.1.3CallableStatement

SQL 语句

-- 创建表
CREATE TABLE student_table (student_id INT PRIMARY KEY,student_name VARCHAR(50) NOT NULL,student_age INT NOT NULL,student_tele VARCHAR(15) NOT NULL
);
​
-- 插入数据
INSERT INTO student_table (student_id, student_name, student_age, student_tele) VALUES (1, 'Tom', 20, '123-456-7890');
INSERT INTO student_table (student_id, student_name, student_age, student_tele) VALUES (2, 'Alice', 22, '234-567-8901');
INSERT INTO student_table (student_id, student_name, student_age, student_tele) VALUES (3, 'Bob', 23, '345-678-9012');
INSERT INTO student_table (student_id, student_name, student_age, student_tele) VALUES (4, 'Charlie', 21, '456-789-0123');
​

Java 示例代码

import java.sql.*;
import java.util.Scanner;
​
public class CallableStatementSQLInjectionExample {public static void main(String[] args) {search();}
​static void search() {String sql = "{CALL getStudentByName('" + getUserInput() + "')}";System.out.println("SQL Statement: " + sql);
​Connection conn = null;CallableStatement callableStmt = null;
​try {conn = Conn.open(); // 假设 Conn 是一个提供数据库连接的类callableStmt = conn.prepareCall(sql);
​// 执行存储过程ResultSet rs = callableStmt.executeQuery();
​// 处理结果集while (rs.next()) {System.out.println("student_id:" + rs.getInt("student_id") + "\t"+ "student_name:" + rs.getString("student_name") + "\t"+ "student_age:" + rs.getInt("student_age") + "\t"+ "student_tele:" + rs.getString("student_tele"));}} catch (SQLException e) {e.printStackTrace();} finally {try {if (callableStmt != null) callableStmt.close();if (conn != null) conn.close();} catch (SQLException e) {e.printStackTrace();}}}
​static String getUserInput() {Scanner scanner = new Scanner(System.in);System.out.print("Enter student name: ");return scanner.nextLine();}
}
​
​

5.2 MyBatis框架介绍

5.2.1 MyBatis了解

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免 了几

乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注 解来配置

和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对 象)为数据库

中的记录。

官方教程:https://mybatis.org/mybatis-3/zh/getting-started.htm

5.2.2 MyBatis基本使用--基于xml实现

实体类Student 各个参数与数据库中目标表的列名一一对应,包括参数名、参数类型

Dao接口文件:

package mybatis.dao;
import mybatis.pojo.Student;
public interface StudentDao {
public Student selectStudent(@Param("name")String name);
}

Mapper xml SQL语句映射文件 :

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 这是一个MyBatis的Mapper文件,定义了与StudentDao接口相关的SQL映射 -->  
<mapper namespace="mybatis.dao.StudentDao"><!-- 定义一个名为selectStudent的查询语句,用于从student_table表中查询学生信息 -->  <!-- resultType指定了查询结果的Java类型,即mybatis.pojo.Student -->  <select id="selectStudent" resultType="mybatis.pojo.Student">select * from student_table where student_name = #{name}<!-- #{name}是一个参数占位符,用于动态传入学生名称进行查询 -->  </select><!-- 这里可以添加更多的SQL映射语句,例如插入、更新、删除等 --> 
</mapper>
​

MyBatis xml 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--  <properties resource="config.properties"/>--><environments default="development"><environment id="development"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/Demo?useSSL=false" /><property name="username" value="root" /><property name="password" value="root" /></dataSource></environment></environments><mappers><mapper resource="mybatis/StudentMapper.xml"/></mappers>
</configuration>

事务管理器(transactionManager)

• 数据源(dataSource)类型有三种:

UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。有点慢,但对那些数据库 连接可用性要求不高的简单应用程序来说,是一个很好的选择。 POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的 连接实例时所必需的初始化和认证时间。

JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在 外部配置数据源,然后放置一个 JNDI 上下文的引用。

测试类

//1. 读取配置文件
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2. 创建SqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
//3. 使用SqlSessionFactory工厂生产SqlSession对象
SqlSession session = sqlSessionFactory.openSession();
try {
//4. 使用SqlSession创建Dao接口的代理对象
StudentDao studentMapper = session.getMapper(StudentDao.class);
//5. 使用代理对象执行方法
Student student = studentMapper.selectStudent("tom");
System.out.println(student);
} finally {
session.close();
inputStream.close();
}

5.3 MyBatis中注入问题

5.3.1 动态SQL

动态 SQL 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前

mybatis 会对其进行动态解析。 Mybatis框架中,接受用户参数有两种方式:

通过${param}方式

通过#{param}方式

#{ }会自动传入值加上单引号,而${ }不会。

例如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.dao.StudentDao">
<select id="selectStudent" resultType="mybatis.pojo.Student">
select * from student_table where student_name = #{name}
</select>
</mapper>

传入name为"tom”

实际提交的SQL为: select * from student_table where student_name = 'tom'

例如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.dao.StudentDao">
<select id="selectStudent" resultType="mybatis.pojo.Student">
select * from student_table where student_name = ${name}
</select>
</mapper>

此时提交name为“tom”,会直接报错, 因为SQL语句中没有给name加引号: select * from student_table where student_name = tom

5.3.2 不安全的${ }

修改mapper文件,拼接引号:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.dao.StudentDao">
<select id="selectStudent" resultType="mybatis.pojo.Student">
select * from student_table where student_name = '${name}'
</select>
</mapper>

提交 ' or 1=1 # , SQL语句成为: select * from student_table where student_name = '' or 1=1 #'

SQL注入防御- 使用#{ }

Mybatis 会处理为: select * from student_table where student_name = ?

5.4 java常见注入场景分析

5.4.1 like 模糊匹配

例如:

<?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper
3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5 <mapper namespace="mybatis.dao.StudentDao">
6 <select id="selectStudent" resultType="mybatis.pojo.Student">
7 select * from student_table where student_name like '%#{name}%'
8 </select>
9 </mapper>

Mybatis便会处理为: select * from student_table where student_name like '%?%'

常见的解决方案[反例】:

例如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.dao.StudentDao">
<select id="selectStudent" resultType="mybatis.pojo.Student">
select * from student_table where student_name like '%${name}%'
</select>
</mapper>

拼接为: select * from student_table where student_name like '%tom%

常见的解决方案[正解】:

#{ } 正确的写法

select * from student_table where student_name like concat('%',#{name}, '%')

最终提交的SQL语句是: select * from student_table where student_name like concat('%','tom', '%')

5.4.2 IN语句

例如: dao文件:定义id为String类型

public interface StudentDao {
public List<Student> selectStudent(@Param("id")String id);
}

Mapper文件:对用户输入预编译:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.dao.StudentDao">
<select id="selectStudent" resultType="mybatis.pojo.Student">
select * from student_table where student_id in (#{id})
</select>
</mapper>

提交 id=“1,2”,实际提交的SQL语句为: select * from student_table where student_id in ('1,2');

修改Mapper文件:改为拼接的方式接收用户输入(${id}) 此时实际提交的SQL语句是:

select * from student_table where student_id in (1,2)

注入测试 :

1) or 1=1 # SQL语句拼接成: select * from student_table where student_id in (1) or 1=1#)

解决方案: #{ } 正确的写法

首先要知道IN后的参数中有多少个元素,然后在语句中写入相同个数的占位符?,这一点在直接 使用

PreparedStatement时非常繁琐,需要编写额外的逻辑获取中元素个数后拼接占位符。

SELECT * FROM student_table WHERE id IN (?, ?, ?, ?)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatis.dao.StudentDao">
<select id="selectStudent" resultType="mybatis.pojo.Student">
select * from student_table where student_id in
<foreach collection="list" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</select></mapper>

dao文件:

public interface StudentDao {
public List selectStudent(@Param("list")List list);
}

传入值为1和2的list变量,实际提交的SQL语句:

select * from student_table where student_id in ('1','2'); 顺利得到id为1和2的数据行。

5.4.3 order by语句

凡是字符串但又不能加引号的位置都不能预编译参数化,这种场景不仅order by,还有group by。执行SQL语句:select * from student_table order by student_age; 数据库表按照student_age列进行默认升序排序 Ø执行SQL语句: select * from student_table order by 'student_age'; 数据库表原样输出,没有任何排序,因为 order by 后跟一个字符串,为常量列排序,而常量列 所有值都相等,所以也就不会排序 。

例如: Mapper文件中定义SQL语句为 select * from student_table order by #{name} 提 name=student_age,因为#{ }会对用户提交的数据自动添加单引号,所以这里实际提交的 SQL语句为:

select * from student_table order by 'student_age'

当我们替换为${ },即SQL语句为: select * from student_table order by ${name} 提交 name=student_age,则实际提交的SQL语句如下,是没有添加单引号的: select * from student_table order by student_age 如愿按 student_age 字段执行排序 .

注入测试

if(1=1,student_age,student_tele) 实际提交的SQL语句为:

select * from student_table order by if(1=1,student_age,student_tele)

说明: order by 中使用数字代替列名是不行的,因为if语句返回的是字符类型,不是整型 即不能替换成 if(1=1,3,4)

SQL语句为: select * from student_table order by if(1=1,3,4)

防御方案

这种场景下,通常采用白名单,只开放有限集合,使用间接对象引用,如果传来的参数不在白名单列表中,直接返回错误即可。 例如: 设置一个字段/表名数组,仅允许用户传入索引值。这样保证传入的字段或者表名都在白名单里面。

5.4.4 group by语句

例如: mapper文件中SQL语句:

select student_id,student_age,max(student_tele) from student_table group by #{name}

dao文件:

public interface StudentDao {
public List<Student> selectStudent(@Param("name")String name);
}

传入name= “student_age”,提交的SQL语句为:

select student_id,student_age,max(student_tele) from student_table group by 'student_age' 相当于常

量列,无法按指定列聚合并输出。

当我们替换为${ },即mapper文件中SQL语句为:

select student_id,student_age,max(student_tele) from student_table group by ${name}

传入name=“student_age”,提交的SQL语句为:

select student_id,student_age,max(student_tele) from student_table group by student_age。

这种场景下解决方案与order by场景一样。

6.4.5 总结

易产生SQL注入漏洞的场景有:

  1. Like 模糊查询: Select * from student where name like ‘%${name}%’

  1. IN 查询: Select * from news where id in (${id})

  1. order by、group by 查询: select * from studentwhere id order by ${id}

6.5 漏洞审计技巧和思路

1、需要关注的点:来自于前端的参数且用户可控且未安全处理拼接到SQL中执行

2、发生场景:(1)在jdbc技术中直接使用“+”拼接 (2)在mybatis中使用$符号拼接 (3) 在hibernate中使用+

需要特别注意一下将从前端获取到的值作为数据sql语句执行的字段名称,而不仅仅是字段的value

String id = requrst.getParmtmter("id")

(1)不常见的sql注入表现形式id = " id = 1",select from user where + id 拼接之后 select * from user where id = 1;

假如id被黑客篡改,id = ‘ ,会导致注入风险(*

(2)常见的sql注入表现形式:select * from user where + id1 = $id;

解决方案:(1)对于必须从前端获取的,梳理一份白名单【key,value】,从前端获得key,通过key从白名单中找到对应的value,将value拼接到sql中。

map = {id:id,id2:id,id3:id,id4:id,id5:id,id6:id}

Strig id = requrst.getParmtmter("id")

假如用户在此位置上,注入了id="id ’",发现id‘不在白名单的范围内,导致无法继续执行sql语句。

(4)因程序员有一定的安全意识,项目中直接拼接比较少,但是在in、like、order by、gruop by等关键字如何直接使用#等预编译处理会报错,所以才不得以使用了拼接,成为了注入的高发点。

3、通过前端页面,代码生成(SQL注入高频爆发点)功能疑似存在SQL注入

4、定位到后端分析相关SQL的执行过程

关于白洁提出的针对order by、group by 白名单修复意见的说明:

1、正常其后应该加的是字段、是因为第一这个字段来自于前端,第二使用了拼接、第三参数可控

2、如果使用#满足不了功能或者是报错,所以程序员不得以使用了拼接

3、允许使用了拼接,但是需要通过白名单修复

(1)一般情况下,传的字段并且固定,允许前来的参数,但是不能直接拼接。

(2)需要在后端匹配与其对应的value,用前端的参数作为key匹配value,最后将value拼接到参数中

Strting name = request.getParemeter("name");

String value = map.getKey("name");

String sql = select * from user order by value;

假如,黑客在前端注入了sql脚本,会导致该Key找不到value,那就是说没办法发生注入了,直接返回给前端输入不合法。

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

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

相关文章

失眠焦虑?这里有你的心灵疗愈手册

&#x1f343; 亲爱的朋友们&#xff0c;你是否也曾在深夜里辗转反侧&#xff0c;无法入眠&#xff1f;是否也曾因为生活中的琐事而焦虑不安&#xff0c;无法放松心情&#xff1f;在这个快节奏的时代&#xff0c;失眠和焦虑似乎成为了我们生活中难以避免的问题。今天&#xff0…

SpringBoot购物网站

摘要 随着信息技术的高速发展&#xff0c;二十一世纪的网络技术和网络应用正在快速融入人们的生活&#xff0c;并且由于网络服务以及网络应用日渐普及&#xff0c;人们对于现在生活的需求也随之增长&#xff0c;而网上购物的便捷对人们的吸引力越来越大&#xff0c;购物网站可…

ES 8.14 向量搜索优化

参考&#xff1a;https://blog.csdn.net/UbuntuTouch/article/details/139502650 检索器&#xff08;standard、kNN 和 RRF&#xff09; 检索器&#xff08;retrievers&#xff09;是搜索 API 中的一种新抽象概念&#xff0c;用于描述如何检索一组顶级文档。检索器被设计为可以…

【经典爬虫案例】用Python爬取微博热搜榜!

一、爬取目标 本次爬取的是: 微博热搜榜 &#xff08;代码也可直接在下方拿&#xff09;&#xff1a; ​ 分别爬取每条热搜的&#xff1a; 热搜标题、热搜排名、热搜类别、热度、链接地址。 下面&#xff0c;对页面进行分析。 经过分析&#xff0c;此页面没有XHR链接通过&am…

如何覆盖!important修饰的属性

最简单的方法 如果这个!important修饰的属性 是自己的写的&#xff0c;去掉这种写法&#xff0c;使用优先级的方式来写这个属性&#xff08;.outter .inner 的优先级就会比 。outter的优先级高&#xff09; 复杂的方法&#xff1a;用魔法打败魔法 但是这个样式来自于全局css&am…

【机器学习】智能创意工厂:机器学习驱动的AIGC,打造未来内容新生态

&#x1f680;时空传送门 &#x1f50d;机器学习在AIGC中的核心技术&#x1f4d5;深度学习&#x1f388;生成对抗网络&#xff08;GANs&#xff09; &#x1f680;机器学习在AIGC中的具体应用&#x1f340;图像生成与编辑⭐文本生成与对话系统&#x1f320;音频生成与语音合成 …

uniapp小程序计算地图计算距离

我们拿到自身和目标距离经纬度 调用此方法即可计算出自身与目标的距离 最后我所展示的页面如下 具体效果可能会有点偏差 要求严格的可以在精细的计算一下

【Python】Redis数据库

Redis数据库 Unit01一、Redis1.1 概述1.2 安装1.3 Redis-cli1.4 数据类型1.5 字符处理1.6 键的命名规则 二、通用命令三、字符串(String)3.1 概述3.2 常用命令3.3 应用场景 四、列表(List)4.1 概述4.2 常用命令 五、集合(SET)5.1 概述5.3 常用命令 六、有序集合6.1 概述6.2 常用…

【行业应用】基于 Arm 虚拟硬件平台实现微信支付二维码识别的智能闸机系统

基于 Arm 虚拟硬件平台实现微信支付二维码识别的智能闸机系统 本实验过程中所显示的优惠价格及费用报销等相关信息仅在【Arm AI 开发体验创造营】体验活动过程中有效&#xff0c;逾期无效&#xff0c;请根据实时价格自行购买和体验。同时&#xff0c;感谢本次体验活动 Arm 导师…

解读自然语言处理:技术、应用与未来展望

引言 自然语言处理&#xff08;Natural Language Processing&#xff0c;简称NLP&#xff09;是计算机科学、人工智能和语言学的一个跨学科领域&#xff0c;致力于实现人与计算机之间通过自然语言进行有效沟通的能力。NLP 的核心任务是理解、解释和生成人类语言&#xff0c;使计…

入门二.HTB--Fawn

大佬 [Hackthebox] Fawn (FTP)_根据您的扫描,目标上运行的 ftp 版本是什么?-CSDN博客 大佬 https://www.cnblogs.com/Hekeats-L/p/16535561.html 任务一 FTP缩写 File Transfer Protocol 任务二 FTP的TCP端口 21 FTP监听的TCP端口号为21,监听的数据端口为20。 任务三 FTP…

Spring技术——介绍、初识

从这篇博客正式开始学习 Spring 。 一、整个Spring技术学习的介绍 首先先对 Spring 做一个简单的认识&#xff0c;并从以下3个方面了解学习Spring 技术的意义&#xff1a; &#xff08;1&#xff09;为什么要学习 Spring 技术&#xff1f; 1、它的市场的占有率很高&#xff…

css之浏览器兼容性

css之浏览器兼容性 内核 内核代表作品 兼容的概念和原因 Hack的副作用 常见的CSS Bug 多个图片在一起&#xff0c;图片套个盒子 图片在IE浏览器上有蓝色边框 解决办法&#xff1a;给img加上 border:0 表单元素中距离顶部元素不一样 左边input 右边button 解决办法&#x…

umijs脚手架

node 16.9.1 注意node版本的问题 node 18.20.0 这个问题其实是node与中端连接出错&#xff0c;无法初始化TTY&#xff08;终端设备&#xff09;&#xff0c;可以用cmd命令行来创建umi项目 nvm管理node https://github.com/coreybutler/nvm-windows/releases 这是nvm-window…

Idea Git中 unversioned files的处理

项目中&#xff0c;使用git commit命令可以查看当前所在的分支&#xff0c;以及当前改动的文件&#xff0c;可以使用快捷键Alt 0打开/关闭&#xff1b;如下图所示&#xff0c; 可以看到分成了两个不同的区域&#xff0c; Changes 表示有改动的文件&#xff0c;包括修改、新增…

Python基础语法学习(工程向)-Stage1

输出的方式&#xff1a; print(fabscwdasd {num}) print(asbduwiu %d, a) print(asnidoian %d %d %d,a,b,c)不换行 print(asbdiuabw,end )输入 a input(输入) 只能输入字符串形式&#xff0c;如果相当做数字用则将其转化为数字 只有合法的数字才能转化成功 a int(input()…

厂里资讯之异步通知文章上下架

kafka及异步通知文章上下架 1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特性ActiveMQRabbitMQRocketMQKafka开发语言javaerlangjavascala单机吞吐量万级万级10万级100万级时效性msusmsms级以内可用性高&#xff08;主从&#xff09;高&#xff08;主从&#…

代码随想录算法训练营第41天|● 01背包问题 二维 ● 01背包问题 一维● 416. 分割等和子集

背包问题 二维01 背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 对于背包问题&#xff0c;有一种写法&#xff0c; 是使用二维数…

Android网络性能监控方案 android线上性能监测

1 Handler消息机制 这里我不会完整的从Handler源码来分析Android的消息体系&#xff0c;而是从Handler自身的特性引申出线上卡顿监控的策略方案。 1.1 方案确认 首先当我们启动一个App的时候&#xff0c;是由AMS通知zygote进程fork出主进程&#xff0c;其中主进程的入口就是Ac…

Mysqld数据库管理

一.Mysqld数据库类型 常用的数据类型 int 整型 无符号[0-4294967296&#xff08;2的32次方&#xff09;-1]&#xff0c;有符号[-2147483648&#xff08;2的31次方&#xff09;-2147483647]float单精度浮点 4字节32位double双精度浮点 8字节64位char固定长度的字符类型…