java之sql注入审计

news2024/12/28 19:53:23

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 configuration
        PUBLIC "-//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.coloradmin.cn/o/1830886.html

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

相关文章

无人机比赛有哪些?

无人机比赛项目可是多种多样&#xff0c;精彩纷呈呢&#xff01; 常见的比赛项目包括S形绕桩赛、平台起降赛、应用航拍、投掷物品和定点飞行等。这些项目不仅考验无人机的性能&#xff0c;更考验飞行员的操控技巧。 在S形绕桩赛中&#xff0c;飞行员需要操控无人机快速而准确…

大数据SQL格式化规范及示例

无论是数据仓库开发还是数据分析&#xff0c;编写清晰易读的SQL是一项基本的技能。本文将分享几个SQL格式的规范和示例&#xff0c;旨在提高SQL的可读性和可维护性。 虽然这些规范没有严格的标准&#xff0c;但统一的格式可以帮助减少理解和维护SQL代码的时间。不管团队其他人…

5G智能运维趋势

随着5G技术的普及&#xff0c;网络运维面临着前所未有的复杂性和数据量挑战。智能运维通过人工智能、大数据分析、自动化工具等技术&#xff0c;为5G网络提供了高效的管理和优化手段。在网络性能优化、故障预测与管理、资源动态调度、安全管理和网络切片管理等方面&#xff0c;…

一些激活函数

一些激活函数 摘要激活函数分类sigmoidTanhSoftsignSoftmaxReLUSoftplusNoisy ReLULeaky ReLUPReluELUSELUSwishGELUGLUGEGLUMishMaxout 摘要 本篇博客对一些激活函数进行总结&#xff0c;以便加深理解和记忆 激活函数分类 饱和激活函数&#xff1a;sigmoid、tanh… 非饱和激…

欣九康诊疗系统助力诊所向数字化转型

数字化已经成为各行各业转型的重点方向&#xff0c;而为了不被时代所淘汰&#xff0c;医疗机构也势必要紧跟潮流&#xff0c;本人作为门诊部的负责人深知医疗机构要想实现数字化转型那么拥有一款便捷实用的医疗平台是必不可少的&#xff0c;近几年&#xff0c;随着国家大力支持…

table组件,前端如何使用table组件,打印数组数据,后端传输的数据应该如何打印

一、如何使用table&#xff0c;将数组数据打印出来 后端传来的数据&#xff0c;很大概率是一个List数组&#xff0c;我们必须用一个table组件&#xff0c;来打印这些数据。 table标签的介绍 在HTML中&#xff0c;table是常用组件之一&#xff0c;主要用来打印数组信息。 它的…

SpringBoot如何自定义启动Banner 以及自定义启动项目控制台输出信息 类似于若依启动大佛 制作教程

前言 Spring Boot 项目启动时会在控制台打印出一个 banner&#xff0c;下面演示如何定制这个 banner。 若依也会有相应的启动动画 _ooOoo_o8888888o88" . "88(| -_- |)O\ /O____/---\____. \\| |// ./ \\||| : |||// \/ _||||| -:- |||||- \| | \\…

基于DPU的云原生裸金属服务快速部署及存储解决方案

1. 背景介绍 1.1. 业务背景 在云原生技术迅速发展的当下&#xff0c;容器技术因其轻量级、可移植性和快速部署的特性而成为应用部署的主流选择&#xff0c;但裸金属服务器依然有其独特的价值和应用场景&#xff0c;是云原生架构中不可或缺的一部分。 裸金属服务器是一种高级…

Training language models to follow instructions with human feedback 论文阅读

论文原文&#xff1a;https://arxiv.org/pdf/2203.02155 论文简介 语言模型越大并不意味着它能更好的理解用户的意图&#xff0c;因此在这篇论文中&#xff0c;展示了根据人的反馈对模型进行微调&#xff0c;使得语言模型能够在各种人物上更好的理解用户的意图。在评估中&…

Linux系统之Ward服务器监控工具

Linux系统之Ward服务器监控工具 文章目录 Linux系统之Ward服务器监控工具介绍资源列表基础环境一、安装Java环境二、下载ward的jar包2.2、下载软件包 三、安装ward工具3.1、启动ward服务3.2、查看你后台启动任务3.3、监听ward服务端口 四、访问ward服务4.1、进入ward初始界面4.…

【可控图像生成系列论文(一)】MimicBrush 港大、阿里、蚂蚁集团合作论文解读

背景&#xff1a;考虑到用户的不同需求&#xff0c;图像编辑是一项实用而富有挑战性的任务&#xff0c;其中最困难的部分之一是准确描述编辑后的图像应该是什么样子。 创新点&#xff1a;在本文作者提出了一种新的编辑形式&#xff0c;称为模仿编辑&#xff0c;以帮助用户更方…

深入理解 Java 中的 volatile 关键字

暮色四合&#xff0c;晚风轻拂&#xff0c;湖面上泛起点点波光&#xff0c;宛如撒下了一片星河。 文章目录 前言一、CPU 三级缓存二、JMM三、并发编程正确性的基础四、volatile 关键字五、volatile 可见性六、volatile 有序性6.1 指令重排序6.2 volatile 禁止指令重排6.3 vola…

虚拟机使用桥接模式网络配置

1、获取本机的网络详细信息 windowr 输入cmd 使用ipconfig -all 一样即可 在自己的虚拟机中设置网络 虚拟机中的ip ---------192.168.36.*&#xff0c;不要跟自己的本机ip冲突 网关-----------192.168.36.254 一样即可 dns -----------一样即可&#xff0c;我多写了几个&am…

数字孪生智慧机场:引领航空未来

图扑数字孪生技术赋能智慧机场&#xff0c;实现运营管理和乘客服务的全面优化。实时数据监控与智能决策助力高效安全的航空体验&#xff0c;推动行业创新与发展。

网络安全:SQL注入防范

文章目录 网络安全&#xff1a;SQL注入防范引言防范措施概览使用参数化查询示例代码 输入验证和过滤示例代码 使用ORM框架示例代码 其他防范措施结语 网络安全&#xff1a;SQL注入防范 引言 在上一篇文章中&#xff0c;我们介绍了SQL注入攻击的基础知识。本文将重点讨论如何防…

【UML用户指南】-16-对高级结构建模-构件

目录 1、概念 2、构件与接口 3、可替换性 4、组织构件 5、端口 6、内部结构 6.1、部件 6.2、连接件 7、常用建模技术 7.1、对结构类建模 7.2、对API建模 构件是系统中逻辑的并且可替换的部分&#xff0c;它遵循并提供对一组接口的实现。好的构件用定义良好的接口来定…

来自工业界的知识库 RAG 服务(四),FinGLM 竞赛冠军项目详解

背景介绍 在 前一篇文章 中介绍过智谱组织的一个金融大模型 RAG 比赛 FinGLM 以及 ChatGLM反卷总局 团队的项目&#xff0c;这篇文章继续介绍下获得冠军的馒头科技的技术方案。 建议不了解比赛背景信息的可以先查看 来自工业界的知识库 RAG 服务(三)&#xff0c;FinGLM 竞赛获…

[YOLOv10涨点改进:注意力魔改 | 轻量级的 Mixed Local Channel Attention (MLCA),加强通道信息和空间信息提取能力]

本文属于原创独家改进:一种轻量级的Mixed Local Channel Attention (MLCA)模块,该模块考虑通道信息和空间信息,并结合局部信息和全局信息以提高网络的表达效果 1.YOLOv10介绍 论文:[https://arxiv.org/pdf/2405.14458] 代码: https://gitcode.com/THU-MIG/yolov10?utm_s…

基于R-Tree的地理空间数据分析加速

几年前&#xff0c;我正在做一个业余项目。我想创建一个 Web 应用程序&#xff0c;推荐当地的特色景点&#xff0c;例如咖啡馆、书店或隐藏的酒吧。我的想法是在地图上显示用户触手可及的所有兴趣点。我的数据集中有数十万个点&#xff0c;我必须巧妙地过滤用户给定范围内的数据…

DVWA - Brute Force

DVWA - Brute Force 等级&#xff1a;low ​ 直接上bp弱口令爆破&#xff0c;设置变量&#xff0c;攻击类型最后一个&#xff0c;payload为用户名、密码简单列表 ​ 直接run&#xff0c;长度排序下&#xff0c;不一样的就是正确的用户名和密码 ​ 另解&#xff1a; 看一下…