51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

对SQL注入漏洞原理的思考

一、一切从输入讲起

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注入-Mx Blog
高级SQL注入-Mx Blog
高级SQL注入备忘单
2年前
0 1940 1
SQL注入攻击-Mx Blog
SQL注入攻击-Mx Blog
SQL注入攻击
9个月前
0 675 238

赞(0)
未经允许不得转载:工具盒子 » 对SQL注入漏洞原理的思考