对SQL注入漏洞原理的思考

admin 2024年6月30日15:51:15评论3 views字数 2712阅读9分2秒阅读模式

一、一切从输入讲起

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: selectobject: userssubject: *condition:    key:username    value:$username

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

admin' and 1=1 #

此时数据库执行的语句

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

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

option:selectobject:userssubject:*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: selectobject: userssubject: *condition:    key:username    value:$username

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

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

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

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

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

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

option: selectobject: userssubject: *condition:    key:username    value:

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

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

原文始发于微信公众号(Daylight庆尘):对SQL注入漏洞原理的思考

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月30日15:51:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   对SQL注入漏洞原理的思考https://cn-sec.com/archives/2877925.html

发表评论

匿名网友 填写信息