【代码审计】SQL注入审计总结

【代码审计】SQL注入审计总结-HackTwoHub社区
【代码审计】SQL注入审计总结
此内容为付费阅读,请付费后查看
600积分
付费阅读

前言

我们要先明确,java是强类型语言,实战我想我们也遇到不少尝试sql注入但是遇到java强制类型要求,参数只能是integer类型的java报错,这种是不存在sql注入的,所以只有变量是String类型的时候,才会存在sql注入。

现在就来看看常规的可以执行sql语句的程序:

JDBC

具体的介绍就不说了,直接进入正题吧!

1、jdbc的java.sql.Statement

Statement是Java JDBC下执行SQL语句的一种原生方式,执行语句时需要通过拼接来执行。若拼接的语句没有经过过滤,将出现SQL注入漏洞

public static void main(String[] args) {
         // 数据库连接信息
         String url = "jdbc:mysql://localhost:3306/security";
         String username = "root";
         String password = "root";
         Connection connection = null;
         Statement statement = null;
         ResultSet resultSet = null;
         try {
             // 加载数据库驱动
             Class.forName("com.mysql.cj.jdbc.Driver");
 
             // 获取数据库连接
             connection = DriverManager.getConnection(url, username, password);
 
             // 创建 Statement 对象
             statement = connection.createStatement();
 
             // 执行 SQL 查询
             String sql = "select * from users where id=1";  //这里是String
             resultSet = statement.executeQuery(sql);
 
             // 处理查询结果
             while (resultSet.next()) {
                 // 假设表中有 id 和 name 两列
                 int id = resultSet.getInt("id");
                 String name = resultSet.getString("username");
                 String pass = resultSet.getString("password");
                 System.out.println("ID: " + id + ", Name: " + name + ", Password: "+pass);
             }
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             // 关闭资源
             try {
                 if (resultSet != null) resultSet.close();
                 if (statement != null) statement.close();
                 if (connection != null) connection.close();
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }

String sql = “select * from users where id=1”;,此处没有进行任何过滤,就会存在sql注入漏洞,此处只是形式,真实情况下,id值是我们可控的!看看预编译的情况!

2、jdbc的PreparedStatement

PreparedStatement使用了预编译的方式,是statement的升级版,更加安全、效率也更高。能有效防止sql注入

public static void main(String[] args) {
         // 数据库连接信息
         String url = "jdbc:mysql://localhost:3306/security";
         String username = "root";
         String password = "root";
 
         Connection connection = null;
         PreparedStatement preparedStatement = null;
         ResultSet resultSet = null;
 
         try {
             // 加载数据库驱动
             Class.forName("com.mysql.cj.jdbc.Driver");
 
             // 获取数据库连接
             connection = DriverManager.getConnection(url, username, password);
 
             // 创建预编译的 SQL 语句
             String sql = "SELECT * FROM users WHERE id = ?";
             preparedStatement = connection.prepareStatement(sql);
 
             // 设置参数
             preparedStatement.setInt(1, 1);
             //如果是int型则用setInt方法,如果是string型则用setString方法
 
             // 执行查询
             resultSet = preparedStatement.executeQuery();
 
             // 处理查询结果
             while (resultSet.next()) {
                 // 假设表中有 id 和 name 两列
                 int id = resultSet.getInt("id");
                 String name = resultSet.getString("username");
                 String pass = resultSet.getString("password");
                 System.out.println("ID: " + id + ", Name: " + name + ", Password: "+pass);
             }
             System.out.println(preparedStatement);
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             // 关闭资源
             try {
                 if (resultSet != null) resultSet.close();
                 if (preparedStatement != null) preparedStatement.close();
                 if (connection != null) connection.close();
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }

使用占位符?,并且进行预编译,锁定了语法树和查询结构。找到无法跳出user表的束缚,导致了无法进行sql注入!

ORM简介

ORM 是一种编程技术,用于在面向对象的编程语言(如 Java、Python)和关系型数据库(如 MySQL、PostgreSQL)之间进行数据映射。它的主要目的是让开发者使用面向对象的方式操作数据库,而不是直接写 SQL。也就是说它不是直接与数据库尽心交互,而是通过操作对象间接操作数据库。

下面的Mybatis、Hibernate就具有ORM框架的属性。

Mybatis下的sql注入

MyBatis 是对 JDBC 的进一步封装与优化,它属于 半自动化的 ORM 框架,相比 Hibernate 灵活性更强,保留了 SQL 的可控性与性能优势。

工作原理简述:

  • 开发者在 Mapper 文件(XML)中提前定义 SQL 语句
  • 在 Java 正文中,只需调用 Mapper 接口方法,框架会自动完成 SQL 执行。
  • MyBatis 本质上是对 JDBC 的封装与升级版,简化了数据库操作流程。

直接步入mybtis的sql查询,根据上面的原理简述,我们的sql语句在Mapper的xml文档中,所以下面的代码也是Mapper文件中的。

安全用法(#{}):

<select id="getUserById" resultType="User">
     SELECT * FROM users WHERE id = #{id}
 </select>

生成的 SQL:

SELECT * FROM users WHERE id = ?

参数通过 JDBC 绑定,无法注入。


危险用法(${}

<select id="getUsersBySort" resultType="User">
     SELECT * FROM users ORDER BY ${sortColumn}
 </select>

如果 sortColumn 是用户输入,传入:

sortColumn=id desc; DROP TABLE users —

最终拼接为:

SELECT * FROM users ORDER BY id desc; DROP TABLE users —

造成严重 SQL 注入漏洞,其利用形同最简单的sql注入

下面我们来看看常见的肯能存在漏洞的几种情况

基础漏洞代码

预编译语法错误导致sql注入,预编译虽然能够防止sql注入,但是如果程序员没有养成良好的习惯,也会存在sql注入的风险

String sql = "SELECT * FROM users WHERE id = ?";
             String username1="null";
             sql = sql + " and username like '%" + username1 + "%'";

如果我的username1传参为user%’ or ‘1’=’1’# ,此时语句就变成了

SELECT * FROM users WHERE id = 1 and username like ‘%user%’ or ‘1’=’1’#%’

返回结果也会是全部信息

图片[1]-【代码审计】SQL注入审计总结 - HackTwoHub社区-HackTwoHub社区

下面就看一看需要用到${}的情况

Order by 注入

还有就是order by注入(order by 字段名)不能使用预编译的写法,因为order by语句后面必须要跟字段名(username、id这种)或者字段列数(1、2这种),字段列数肯定是可以预编译的,因为它是Integer类型,但是如果要采用字段名进行排序,就会出现问题,因为setString(1,username)后会自动给usernname套一个引号,而引号会让数据库认为这是一个字符串,而不是字段名,导致order by语句失效,字段列数就不会导致无法使用预编译,如下就是使用预编译的情况下的order by情况:

图片[2]-【代码审计】SQL注入审计总结 - HackTwoHub社区-HackTwoHub社区

 

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容