浅谈预编译 | SQL注入防御

2024-09-02 56 0

前言

一提到SQL注入的防御,首先想到的就是预编译咯,但是我之前并没有仔细研究过预编译,于是今天来谈一谈预编译是如何防御SQL注入的?预编译真的能防御所有的SQL注入吗?

以php+mysql为例

SQL语句的执行流程

在执行 SQL 语句时,数据库通常经历以下步骤:

  1. 解析(Parsing):数据库接收到 SQL 语句后,首先对其进行关键字、标识符、运算符的分解,然后进行解析和语法分析。这一步骤的目的是检查 SQL 语句的语法是否正确,并将其转换为一个解析树或语法树。

  2. 预处理(Preprocessing):解析后的 SQL 语句会进行预处理,包括检查语句中涉及的表和字段是否存在,用户是否有足够的权限来执行该操作,同时检查语义的正确性等。

  3. 优化(Optimization):在这个阶段,数据库优化器会根据数据库的统计信息和索引等信息,生成一个高效的查询执行计划。优化的目的是选择最优的执行路径,以提高查询性能。

  4. 生成执行计划(Execution Plan Generation):优化器生成的执行计划被传递给执行引擎,执行引擎会将其转化为具体的操作步骤和访问路径。

  5. 执行(Execution):执行引擎根据生成的执行计划,实际执行 SQL 语句,包括访问数据、进行计算和更新数据等操作。

  6. 返回结果(Result Return):执行完成后,结果会被返回给用户。对于查询操作,结果通常是数据集;对于修改操作,结果通常是操作成功或失败的状态。

  7. 事务管理(Transaction Management):如果 SQL 语句是在事务中执行的,数据库会在执行完成后根据事务的状态(如提交或回滚)来确保数据的一致性和完整性。

总结起来就是:先进行SQL执行语句的编译,再执行编译好的SQL语句,再反馈执行结果

SQL注入产生的原理

以这样一个查询用户信息的SQL注入为例:

select user_info from users_account where key = $_GET['key']

直接接收GET参数key,从users_account表中查询user_info字段,如果key正确就返回对应用户的个人信息

正常的查询是这样:输入正确key,返回对应的用户信息

用户输入正确的key:123456

select user_info from users_account where key = 123456

但是恶意的查询是这样:输入恶意SQL片段,返回所有用户信息

用户输入SQL注入的pyload:1 or 1=1

select user_info from users_account where key = 1 or 1=1 

这样就从语义上,改变了整个SQL语句的执行逻辑,由于where key = $_GET['key']此处用户完全可控,又毫无过滤,于是攻击者可插入SQL语句片段来构造出一个完整的恶意的查询,改变了原来的SQL语句的执行逻辑,改变了语法树,从而达到攻击者的恶意目的

什么是预编译

预编译语句是一种在执行 SQL 语句之前,将 SQL 语句的结构和查询逻辑预先处理的技术。它与直接执行 SQL 语句相比,具有以下优点:

  1. 防止 SQL 注入:预编译语句将 SQL 语句的结构和数据分开处理,数据作为参数传递,而不是直接嵌入到 SQL 语句中。这种方式有效地防止了 SQL 注入攻击。

  2. 提高性能:当使用预编译语句时,数据库可以缓存和重用查询的执行计划,从而提高查询性能,尤其是在相同的查询结构但不同的参数被多次执行时。

预编译的工作流程

  1. 准备语句:SQL 语句的结构被发送到数据库服务器,数据库服务器解析并预处理这些语句,生成一个查询执行计划。

  2. 绑定参数:在预编译阶段,参数占位符(如?:param)被用作数据的占位符,实际的参数值在执行阶段绑定到这些占位符。

  3. 执行语句:将实际的参数值传递给预编译语句,然后执行查询,数据库使用之前生成的执行计划来处理实际的数据。

  4. 获取结果:查询执行后,结果返回给 PHP 脚本,可以进一步处理或显示。

预编译代码示例

使用 PDO 的预编译语句

<?php
// 创建 PDO 实例
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');

// 预编译 SQL 语句
$stmt = $pdo->prepare('select user_info from users_account where `key` = :key');

// 绑定参数
$stmt->bindParam(':key', $key);

// 设置参数值
$key = $_GET['key'];

// 执行查询
$stmt->execute();

// 获取结果
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

print_r($results);
?>

使用 MySQLi 的预编译语句

<?php
// 创建 MySQLi 实例
$mysqli = new mysqli('localhost', 'username', 'password', 'testdb');

// 检查连接
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
}

// 预编译 SQL 语句
$stmt = $mysqli->prepare('select user_info from users_account where `key` = ?');

// 绑定参数
$stmt->bind_param('s', $key);

// 设置参数值
$key = $_GET['key'];

// 执行查询
$stmt->execute();

// 获取结果
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);

print_r($rows);

// 关闭语句和连接
$stmt->close();
$mysqli->close();
?>

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

预编译技术是提前写好了SQL语句的语法结构,或者叫"模板",所有的用户输入都只是当作参数插入,导致所有试图改变SQL语法结构的SQL注入payload都只是一个参数而已,无法逃逸出来,无法对SQL的语法结构、执行逻辑造成任何影响

SQL注入之所以能成功是因为,可控之处改变了整个SQL语句的语法结构,改变了整个SQL语句的执行逻辑,也就是可以自由控制"模板"

但是如果使用了预编译,语法结构早已提前设定好,无法被修改或影响其执行逻辑,所有的攻击语句都失效了,都只是一个参数,一串字符串而已

没有使用预编译:1 or 1=1

select user_info from users_account where key =  1 or 1=1 

此处的 1 or 1=1,就成功的改变了SQL语句的语法结构,改变了执行逻辑

使用了预编译:1 or 1=1

$stmt = $mysqli->prepare('select user_info from users_account where `key` = ?');
select user_info from users_account where key = 【1 or 1=1】

实际执行时,SQL语句的语法结构早已设定好,无法被影响,这里的【1 or 1=1】仅仅是一个参数,一串字符串,不会被解析为SQL语句的一部分

预编译演示

  1. 本地创建测试数据库和表,随意填充一点数据

浅谈预编译 | SQL注入防御插图


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

靶场战神为何会陨落?
漏洞分析与实战分享 | 深入探讨pkexec提权漏洞
postMessage常见安全问题
[Meachines] [Medium] Lightweight LDAP密码嗅探+TRP00F 自…
ofcms V1.2 代码审计(一)
网安内家拳——东西向网络安全是门高级功夫

发布评论