长亭百川云 - 文章详情

对SQL注入漏洞原理的思考

庆尘qc

43

2024-06-24

一、一切从输入讲起

1、信任问题

安全问题的本质就是信任问题

例如对一个网站的开发来说

  • 信任普通用户的输入——前台漏洞

  • 信息管理员用户的输入——后台漏洞

  • 信任离线升级/在线升级/自动化升级/升级包——供应链攻击

  • 不信任任何输入——信任代码逻辑——逻辑漏洞

2、防御核心

对应的安全方案有很多,但都可以简化为

输入——检测是否存在风险——输出

可以看到,一切都与输入息息相关,毕竟对于黑客来说,能控制的也只有输入,所以挖掘漏洞最好的入口点,也就是从输入开始

二、控制流与数据流

1、介绍

可以将程序员的代码分割为两个部分

  • 一部分是控制代码走向的控制流代码

  • 另一部分是用来展示,存储,流转的数据流(包括输入的数据和程序员设定的数据)

2、思考

  • 为什么要强调数据流操作的这个"被"字?

  • 主谓宾”层面,数据流的数据不应该有主动权

  • 这个控制流数据流如何帮助我们更好地去理解输入产生的漏洞?

  • 程序员希望用户输入的一定是数据流,而不是控制流

  • 一旦我们输入的数据(数据流)能够以某种方式侵入到控制流时,漏洞就随之产生了

三、SQL注入的基础代码样例

先来看看这样一段存在SQL注入的PHP的代码

<?php  
    $db \= init\_db();  
    $username \= $\_GET\['username'\];   
    $db\->query("select \* from users where username = '$username'");   
?>

看看这段代码的输入流转:

输入——php字符变量——SQL语句——数据库

程序员如果没考虑到这里的安全问题的话,他的控制流程想法如下

option: select
object: users
subject: *
condition:
    key:username
    value:$username

如上,程序员的想法是用户的输入只能影响结构中的value位置,但是我们通过输入恶意代码,就能够跳出数据流,从而影响到控制流,例如我们输入的username为

admin' and 1=1 #

此时数据库执行的语句

select * from users where username='admin’ and 1=1 #'

思考:那现在实际执行的控制流程跟程序员想法中的控制流程一样吗?

option:select
object:users
subject:*
condition:
    expression: and
        key1:username
        value1:admin
        key2:1
        value2:1

如上,我们输入的数据被带入数据库中执行,从而导致数据流入侵到了控制流

所以程序员在编程时应该保证用户的数据只能影响数据流,例如上面的value,如果不能保障,那么就会出现漏洞

拓展:

  • 这里就引出了代码审计的两个核心

  • 能否让数据流逃逸到控制流

  • 业务逻辑可能产生的点在哪(后面再介绍)

四、核心思维

1、是什么

想方设法去执行一条完整的SQL语句,把数据带出来或把命令传进去

2、SQL注入需要关注的点

  • 编程语言

  • 不同编程原因最终目的都是为了将payload送进数据库层进行执行,能看到注入点即可,语言不重要

  • 注入类型

  • 比编程语言更重要一些,但其中所有的注入类型都是sql语句不同的写法而已

  • 产生注入的输入点

  • 条件?

  • 客体(字段)?

  • 对象(表名)?

  • ......

  • 可以理解为我们输入的数据处于SQL语句的哪个位置

3、注入挖掘三板斧

  • 仅仅抓住输⼊

  • 当数据流⼊侵到控制流时,漏洞就产⽣了

  • “数据流⼊侵控制流”产⽣的⻛险点,在于不同层⾯组件的交汇处(如:代码层与数据库层)

是不是有点看不懂?没关系,更简单的总结如下

  • 找输入点——哪些位置可能存在注入

  • 构造payload——如何写入或者执行自己的恶意代码

  • 找数据库交互的位置——哪些功能可能存在注入?

五、预编译简述

先来看看PHP预编译防止SQL注入的案例的

// 创建连接
  $conn = new mysqli($servername, $username, $password, $dbname);
 
// 获取用户输入
  $user_input = $_GET['user_input'];
// 使用预编译语句和参数绑定来防止 SQL 注入
  $stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
  $stmt->bind_param("s", $user_input);
  $stmt->execute();
$result = $stmt->get_result();

来看一段Java预编译防止SQL注入的案例

// 创建数据库连接
  Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "password");
// 使用预编译语句
  String query = "SELECT * FROM users WHERE username = ?";
  PreparedStatement pstmt = conn.prepareStatement(query);
  pstmt.setString(1, username); // 设置第一个占位符的值
// 执行查询并处理结果
  ResultSet rs = pstmt.executeQuery();
  while (rs.next()) {
System.out.println("User found: " + rs.getString("username"));
  }

定了了正常的SQL语句之后,控制逻辑如下:

option: select
object: users
subject: *
condition:
    key:username
    value:$username

为什么需要使用预编译?——因为要防止SQL注入

  • 为什么要防止SQL注入?——防止数据流入侵到控制流

  • 怎样防止数据流入侵到控制流?——将用户的输入限制到输入流

预编译为什么能防止SQL注入

预编译语句通过将SQL查询语句参数分开,使用占位符来代表参数,然后将用户输入的数据绑定到占位符上,确保了输入数据被正确地解释为字符串而不是SQL代码。因此,无论用户输入什么数据,都不会影响原始查询的结构和意图,从而有效地防止了SQL注入攻击。

也就相当于把下面这部分进行固定了,不允许改动

option: select
object: users
subject: *
condition:
    key:username
    value:

只允许用户控制$username,这样就保证了用户的输入只在数据流生效,而无法影响到控制流,这样自然就能防止SQL注入

好了,关于SQL注入的研究暂时就到这里了,后面有新的理解的话会在这篇文章中更新,有不对的地方欢迎各位师傅指正

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2