一、一切从输入讲起
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注入备忘单
2年前
0 1940 1
SQL注入攻击
9个月前
0 675 238