SQL预编译——预编译真的能完美防御SQL注入吗

【网络安全】SQL注入详解 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。 阅读详情
AI摘要

本文介绍了SQL注入原理,指出预编译可解决大部分SQL注入问题。预编译是先将SQL语句结构告知数据库服务器,再单独传参数执行。但预编译并非能完美防御,对于不可参数化位置(如order by后)无法防御,还分析了模拟预编译的注入点及应对方法。

SQL注入原理

sql注入是指攻击者拼接恶意SQL语句到接受外部参数的动态SQL查询中,程序本身
未对插入的SQL语句进行过滤,导致SQL语句直接被服务端执行。
拼接的SQL查询例如,通过在id变量后插入or 1=1这样的条件,来绕过身份验证,获
得未授权数据的访问权。

SELECT * FROM user WHERE id = -1 or 1=1

由于or 1=1 满足永真结果,sql语句会执行输出user中的全部内容。

那么这么危险的漏洞,有没有办法进行阻止呢

有的兄弟,有的

预编译就能解决大部分的SQL注入问题

什么是预编译(Prepared Statement)?

预编译就是在执行 SQL 前,把 SQL 语句先告诉数据库服务器,编译好结构,然后再单独传参数进去执行!

它的全名叫:

Prepared Statement(预处理语句 / 预编译语句)

正常写 SQL 是怎样的?

我们先看看普通的拼接 SQL 是怎样的:

username = input("请输入用户名:")
sql = "SELECT * FROM users WHERE username = '" + username + "'"
cursor.execute(sql)

这就好像直接把“用户输入”和“SQL语句”拼成一整句话。
用户只要输入了奇怪的东西,就能控制整个 SQL 的逻辑!Σ(っ °Д °;)っ

使用预编译是这样写的:

username = input("请输入用户名:")
sql = "SELECT * FROM users WHERE username = ?"
cursor.execute(sql, (username,))

重点就是!
SQL 写的时候,用 占位符(?) 或者 命名参数(:name),
参数是后面传进去的!不是拼进去的!

预编译的执行流程(详细版!)

  1. 发送 SQL 模板给数据库服务器
    比如:
SELECT * FROM users WHERE username = ?

这个时候数据库就把这个 SQL 的结构编译好了,生成了“执行计划”

  1. 服务器把这个语句存起来
    存的是“只差参数”的 SQL 模板。
  2. 客户端发送参数
    比如:
("admin",)
  1. 数据库执行之前编译好的 SQL
    把你传进去的参数当成“纯数据”,直接放进语句执行!

为什么这样能防止 SQL 注入?

因为参数永远只是值,不会被当作 SQL 代码执行!
哪怕用户输入的是:

' OR '1'='1

数据库也会当成一个完整的字符串 ' OR '1'='1 来处理,它不会让它改变 SQL 语句的逻辑结构

但是预编译真的能完美防御SQL注入吗?笔者在写这篇文章前一直没有思考过这个问题,一是因为知识面浅薄,没有想这么多;二是因为确实没怎么研究过防御漏洞相关的知识,直到翻到了某篇blog预编译与sql注入 – fushulingのblog再谈预编译与sql注入 – fushulingのblog

假设就用上面的例子,例子中 where语句中的内容是被参数化的。这就是说,预编译仅仅只能防御住可参数化位置的sql注入。那么,对于不可参数化的位置,预编译将没有任何办法。

那么不可参数化的位置都有哪些?

表名、列名
order by、group by
limit
join
等

我们以order by举例,现在有一个sql语句如下(以下为伪代码)

SELECT * FROM users ORDER BY {user_input};

其中user_input是传递过来的参数,例如 id

SELECT * FROM users ORDER BY id;

这个语句是正确的,但是如果user_input输入 id;drop table users --

SELECT * FROM users ORDER BY id;drop table users --

这样就被成功注入了,而这种位置是不可被参数化的,所以是无法通过预编译防御的。

SQL预编译中order by后为什么不能参数化原因 - 诸子流 - 博客园

这篇文章中提到

大概就是说order by的后面是字段,字段不能用引号,但是预编译又只有用引号的setString()这一种方法,所以导致一切是字符串但又不能加引号的位置都不能参数化

原文以java为例进行说明,但是php中又是怎样呢

模拟预编译

网上一般讲的预编译是这么写的:

<?php
$username = $_POST['username'];
$db = new PDO("mysql:host=localhost;dbname=test", "root", "root123");
$stmt = $db->prepare("SELECT password FROM test where username= :username");
$stmt->bindParam(':username', $username);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($result);
$db = null;
?>

这里如果post传参username=root,就可以正常查到值,但是传'root'就查不到,通过查看日志可以发现在sql执行的过程中其实根本没有参数绑定、预编译的过程,本质上只是对符号做了过滤

这里参考文献中的作者将其称为虚假的预编译

为什么开发者要做一个虚假的预编译呢,那是因为一个参数——PDO::ATTR_EMULATE_PREPARES,这个选项用来配置PDO是否使用模拟预编译,默认是true,因此默认情况下PDO采用的是模拟预编译模式,设置成false以后,才会使用真正的预编译。开启这个选项主要是用来兼容部分不支持预编译的数据库(如sqllite与低版本MySQL),对于模拟预编译,会由客户端程序内部参数绑定这一过程(而不是数据库),内部prepare之后再将拼接的sql语句发给数据库执行。

真正的预编译

我们在原先的代码上把ATTR_EMULATE_PREPARES设为false取消模拟预编译

<?php
$username = $_POST['username'];

$db = new PDO("mysql:host=localhost;dbname=test", "root", "root123");
$db -> setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$stmt = $db->prepare("SELECT password FROM test where username= :username");

$stmt->bindParam(':username', $username);

$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($result);

$db = null;

?>

我们post一个username=root

这时数据库中执行的顺序变成了:先连接,然后准备语句,用问号?占位,接着用输入替换问号?执行语句,专业点的说法叫做:

  1. 建立连接;
  2. 构建语法树;
  3. 执行

这也是为什么我们之前说的,预编译的作用是让整个语句的功能已经提前定死,消除了sql语句的歧义。当我们输入username= ‘root’同样会没有任何输出

模拟预编译的注入点

宽字节注入

2023-10-22T13:12:13.619960Z	    9 Query	SELECT password FROM test where username= '\'root\''

从模拟预编译的日志,我们可以发现这里仅仅是用到\的转义,所以我们是否可以进行宽字节注入呢

答案当然是可以的吗,但是我没复现

没有参数绑定

没有参数绑定的预编译等于没有预编译,无论是真编译还是模拟预编译,没有参数绑定等于没编译,并且由于pdo默认支持堆叠注入,我们可以通过堆叠注入先插入值然后查询插入的值获取输出结果。

这两个的复现具体可以看下面这个文章:

预编译与sql注入 – fushulingのblog

--------------------------------------------------------------------------------------

对于order by、ground by这种无法进行预编译的场景我们该怎么防御呢,比如Mybaits必须使用${}order by参数,可通过白名单思路对传入的参数进行判断,或者使用间接对象引用,前端传递引用数字等,用于与后端排序参数做数组映射,避免前端直接传入order by参数造成sql注入。

比如我们想执行select xx order by name,那么前端就不要传入name这个值,而是数字比如1,然后在后端将1与真正想查询的参数name进行对应,然后再执行sql语句。比如映射表为1->name,2->age,3->gender,想要查询order by name、age、gender的结果前端只用传入1、2、3即可,通过防止直接执行用户传入的值来从根本上防止sql注入的产生。

ps:order by后面以及group by 后面的注入,有报错回显的直接报错注入就行了,这个简单,没有报错的话我们可以通过构造布尔条件进行注入:随rand()中值真假的不同,排序出来的结果也是不同的,因此可以通过这个特征进行布尔注入,比如输入rand(ascii(mid((select database()),1,1))>96),如果成立和不成立输出结果显然是不同的,如果我们成功注入,输出应该是root dingzhen admin的顺序

SQL注入——SQL注入具体防御方案 1.GPC/RUNTIME魔术引号 magic_quotes_gpc负责对GET,POST,COOKIE的值进行过滤。 magic_quotes_runtime对从数据库或者文件中获取的数据进行过滤。 通常在开启这两个选项之后能防住部分SQL注入漏洞被利用,因为我们之前也介绍了,在某些环境下存在绕过,在INT型注入上是没有多大作用的。 通常数据污染有两种... 阅读详情

相关推荐

【面经】预编译为什么可以防止sql注入,mybatis是如何预防的

安全工程师一面遇到的问题: 1、sql注入怎么防御? 答:sql语句预编译 为什么预编译能防止sql注入? 吐血!!平时只记着sql注入的防御方法是预编译,没想到问这个还答不上来。 赶紧补一补,下面是我的个人理解,如有哪里写的不对 ,恳请各位大牛斧正。 首先我们要知道执行一条sql命令的过程: 一条sql语句的执行需要经过语义解析,制定执行计划,执行并返回结果。 预编译是指把要执行的sql语句先进行一个解析,解析语法以及确定查询范围还有查找的返回结果类型,就是确...

weixin_45525005的博客 1687

SQL预编译

1.数据库预编译起源 (1)数据库SQL语句编译特性: 数据库接受到sql语句之后,需要词法和语义解析,优化sql语句,制定执行计划。这需要花费一些时间。但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。 (2)减少编译的方法 如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。为了解决上面的问题,于是就有了预编译,预编译语句就是将这类

weixin_47804371的博客 6942

预编译SQL防止SQL注入

回顾一下全文,当我们说“预编译”的时候,其实这个功能来自于数据库的支持,它的原理是先编译带有占位符的 SQL 模板,然后在传入参数让数据库自动替换 SQL 中占位符并执行,在这个过程中,由于预编译好的 SQL 模板本身语法已经定死,因此后续所有参数都会被视为不可执行的非 SQL 片段被转义,因此能够防止 SQL 注入。当我们通过 JDBC 使用执行预编译 SQL 的时候,此处的预编译实际上是假的预编译(至少 MySQL 是如此,不过其他数据库仍待确认),

shengyin714959的博客 1679

sql预编译

1、什么是预编译 1.1、 sql的执行过程 ① 词法和语义分析② 优化sql语句,指定执行计划③ 执行并返回结果我们把这种普通语句称作Immediate Statements。 select colume from table where colume=1; select colume from table where colume=2; 但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同,那么这个时候会对上面两条语句生成两个执行计划,一千次查询就需要一千个执行计划,

Mark 4930

【网络安全 | SQL注入】一文讲清预编译防御SQL注入原理

预编译又称为预处理,顾名思义,就是为代码编译做的预备工作。预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。 举个例子,很多情况下,一条SQL语句可能会反复执行,或者每次执行的时候只有个别的参数值不同,如:

等风来 7586

用PreparedStatement 预编译的 SQL 就不会有被 SQL 注入的风险?

当我们说到关于持久层框架的功能,必然需要先想想这个功能的源头到底是不是直接通过数据库提供的。实际上和事务一样,SQL 预编译的功能也是需要数据库提供底层支持的。以 MySQL 为例,在 MySQL 中,所谓预编译其实是指先提交带占位符的 SQL 模板,然后为其指定一个 key,MySQL 先将其编译好,然后用户再拿着 key 和占位符对应的参数让 MySQL 去执行,用法有点像 python 中的 format 函数。一个标准的预编译 SQL 的用法如下:先通过 设置一个 SQL 模板,然后通过 提交参

数字化小李的博客 994

预编译真的能完美防御SQL注入吗?

在之前面试的时候,我还是太菜了,有一道面试题还是值得研究研究的。面试官问我,怎样完全避免sql注入?我想起了刚毕业时校招有一次也被问过一样的问题,当时我说的是:“基本上预编译就能解决了。”当时并没有对我提出质疑,所以我就一直以为预编译是无敌的,可以绝对防御sql注入。所以这次被问到,我依旧回答了预编译,不过这次面试官没有放我一马,开始对我进行追问:“有没有什么情况是预编译无法解决的?”我一听就知道完蛋了,自己才学疏浅终于还是掉坑里了,然后才意识到自己根本没有去深入研究过预编译。所以这次就想记录一下。

白天安全建设,晚上全栈开发,大模型爱好者。公众号:飞羽技术工坊。 2903

WhatsApp注册流程逆向分析:从协议解析到风控对抗实战

移动应用安全分析的核心在于理解客户端与服务端的交互协议与验证机制。通过逆向工程,可以深入剖析应用的数据加密、传输与身份验证原理,这对于构建安全模型、进行自动化测试和风控策略研究具有重要技术价值。在即时通讯、金融支付等高安全要求场景中,掌握应用注册流程的底层实现是评估其安全性的关键。本文聚焦于使用动态分析工具如Frida进行Hook和流量抓包,结合设备指纹识别与模拟器检测技术,详细解析了大型应用的注册风控体系,为安全研究人员和开发者提供了从环境搭建到实战对抗的完整方法。

weixin_33935636的博客 316

SQL注入:使用预编译防御SQL注入时产生的问题

预编译防御SQL注入是一种常用的防御SQL注入攻击的方法,通过将SQL查询语句预先编译成一个模板,然后将用户输入的数据作为参数传递给模板,从而避免了直接拼接用户输入数据到SQL查询语句中。然而,预编译防御SQL注入也存在一些问题。首先,预编译语句的性能问题是一个挑战,因为预编译语句需要在数据库中进行编译和优化,这可能会增加数据库的负担和查询时间,影响系统的性能。其次,预编译语句的复杂性也是一个问题,因为需要事先定义好SQL查询语句的结构,然后将用户输入的数据与模板进行匹配,增加了代码的复杂性和维护成本。

qq_68163788的博客 3640

从 0 到 1,实战 Power BI 电商销售可视化看板全流程

本文详细介绍了从零开始使用Power BI构建电商销售可视化看板的全流程,涵盖数据准备、建模、可视化设计及高级功能应用。通过实战案例展示如何将复杂销售数据转化为直观图表,帮助电商从业者快速掌握销售趋势、热销产品和用户画像等关键信息,提升数据分析效率。

weixin_29061041的博客 485

逐行逐句进一步了解SurroundOcc(一)

通过分析中的模型构建过程,理解了从配置文件到模型实例化的整个流程。构建过程的核心是通过build和等函数,逐步将配置文件解析并生成模型的各个模块对象,最终组合成一个完整的模型结构。这一过程涉及多个类和函数的调用,其中函数是生成对象的关键,它能够从配置字典中提取必要信息并生成对应的类实例。

lovely_yoshino的博客 8112

浅谈SQL注入的四种防御方法

1.限制数据类型 2.正则表达式匹配传入参数 3.函数过滤转义 4.预编译语句

NLost 2万+

用OpenPose进行单个或多个人体姿态估计

在计算机视觉中,人体姿态估计(关键点检测)是一个很常见的问题,在体育健身、动作采集、3D试衣、舆情监测等领域具有广阔的应用前景,本文要使用的检测方法就是基于OpenPose的人体姿态估计方法。 一、OpenPose简介 OpenPose人体姿态识别项目是美国卡耐基梅隆大学(CMU)基于卷积神经网络(CNN)和监督学习(SL)并以Caffe为框架开发的开源库。可以实现人体动作、面部表情、手指运动等姿...

weixin_42709563的博客 8404

PDO预编译与sql注入

刚学web安全的时候学到sql注入防御,那些文章基本上都会说利用pdo预编译就可以近乎完美防御sql注入,或者看到一些渗透经验贴,遇到sql经过预编译的网站师傅们总是会建议赶紧换个站,那么预编译究竟能不能完美防御sql注入,或者说预编译下的sql注入有什么奇技淫巧吗?首先是第一个问题,为什么预编译或者说参数化查询可以防止sql注入呢?我之前看过的一个面经上是这么写的:使用参数化查询数据库服务器不会把参数的内容当作 sql 指令的一部分来执行,是在数据库完成 sql 指令的编译后才套用参数运行。

qq_64395221的博客 1774

COLMAP在Ubuntu下的编译陷阱与实战避坑指南——从环境配置到三维重建全流程

本文详细介绍了在Ubuntu系统下编译和配置COLMAP三维重建工具的全流程,包括环境准备、依赖库安装、CUDA加速配置以及常见编译问题的解决方案。通过实战案例演示了从图像特征提取到稠密重建的完整步骤,并提供了性能优化和问题排查的实用技巧,帮助用户高效完成三维重建任务。

weixin_29003437的博客 1048

【HAL库】STM32CubeMX开发----STM32F407----ETH+LAN8720A+LWIP----ping通

本次实验以芯片为MCU,使用25MHz外部时钟源。以太网PHY层芯片为LAN8720A,由MCU引脚PA8提供时钟。

MQ0522的博客 4843

网络安全-SQL注入原理、攻击及防御

目录 SQL注入原理 SQL注入条件 防御SQL注入的方法 使用预编译语句 使用存储过程 检查数据类型 使用安全函数 SQL注入原理 程序员没有遵循代码与数据分离原则,使用户数据作为代码执行。 SQL注入条件 用户可以控制数据的输入。 原本要运行的代码拼接了用户的输入。 防御SQL注入的方法 使用预编译语句 绑定变量,攻击者无法改变SQL的结构。不同的编程语言Java、Php有不同的语法,就不做展示了。 使用存储过程 使用安全的存储过程对抗SQL注入,由于存储过程中也可能存在

关注网络安全、云原生安全 3万+

基于MATLAB开发AUTOSAR软件应用层模块-part2.建立一个AUTOSAR的模型,并生成autosar格式代码

上一篇我们建立了一个空模型,这篇我们接着上篇文章,建立一个简单的模型 点击 Library Browser,打开simulink模块库,见下图 左侧是模块库的列表,右侧是每个模块库里包含的具体的模块。 点击通用模块库,里边是一些常见的模块, 我们选取里边的3个模块,分别是输入,输出和增益三个模块,右键点击模块,选择add block to model xx,可以把模块添加到模型中。 然后练好连线 一个简单的模型就搭建好了,之后我们修改配置,将模型修改为AUT...

汽车软件开发 3303

防御SQL注入的方法总结

这篇文章主要讲解了防御SQL注入的方法,介绍了什么是注入,注入的原因是什么,以及如何防御,需要的朋友可以参考下 SQL 注入是一类危害极大的攻击形式。虽然危害很大,但是防御却远远没有XSS那么困难。 SQL 注入可以参见:https://en.wikipedia.org/wiki/SQL_injection SQL 注入漏洞存在的原因,就是拼接 SQL 参数。也就是将用于输入的查询...

weixin_34186128的博客 1563

Coverity本地增量C/C++代码检查(Linux)操作简介

Coverity 是一款精确的综合静态分析与应用安全测试 (SAST) 平台,它可以在编写代码时发现关键的缺陷和安全缺陷,防止它们变成安全漏洞、故障或维护缺陷。

1483

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【SQL性能优化】预编译SQL:从注入防御到性能飞跃

🔥这时如果用户输入是' OR '1'='1,整个数据库将门户大开!🚨,既安全又高效!

2401_83194332的博客 1678

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

SQL注入——预编译CASE注入

预编译 CASE(Prepared CASE)是一种数据库查询语言(如SQL)中的控制语句,它可以根据条件表达式的值来选择执行不同的语句块,类似于编程语言中的 switch 语句。预编译 CASE 注入则是一种基于预编译 CASE 语句的安全漏洞攻击。预编译 CASE 注入攻击的漏洞原理与其他类型的注入攻击相似,即攻击者尝试构造特定的输入数据来绕过应用程序的输入验证和过滤机制,从而能够执行未经授权的操作或获得敏感信息等。在预编译 CASE 注入攻击中,攻击者通常会针对应用程序中存在的具有条件判断语句

来日可期的博客 2260

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

SQL 注入防御方法总结

SQL 注入是一类危害极大的攻击形式。虽然危害很大,但是防御却远远没有XSS那么困难。 SQL 注入可以参见:https://en.wikipedia.org/wiki/SQL_injection SQL 注入漏洞存在的原因,就是拼接 SQL 参数。也就是将用于输入的查询参数,直接拼接在 SQL 语句中,导致了SQL 注入漏洞。 1. 演示下经典的SQL注入 我们看到:select i...

阳光灿烂的日子的博客 9144

预编译SQL:防止SQL注入

在这段代码中,我们通过字符串拼接的方式构建了静态SQL语句,并使用 对象的 方法来执行该查询。 为占位符设置参数:接下来,通过 对象的 方法为占位符赋值。其中,索引从1开始,并且方法中的数据类型要与参数实际的数据类型相匹配。例如: 执行SQL:完成参数设置后,直接调用 (用于执行查询语句,即DQL)或 (用于执行插入、更新、删除语句,即DML)方法来执行SQL语句,无需再次传入SQL语句。例如: 预编译SQL的核心优势深度解析 优势一:彻底防御SQL注入攻击(原理与案例) SQL注入原

2401_86968005的博客 1055

SQL 注入与预编译防御

先确定 SQL 结构,再填入数据数据库在接收到 SQL 时,先对语句中的结构(如表名、条件列)进行编译,然后等待参数填充。用户提供的数据被视为纯数据,而不是可执行的 SQL 片段。SQL 注入 = 代码中拼接了用户输入 → 导致攻击者能改变 SQL 逻辑。防御核心 =分离代码与数据。在 JDBC 中,应禁止使用 Statement + 拼接字符串。使用(预编译)来绑定参数。对动态结构(如表名、列名、排序字段)必须手动校验或使用白名单。

rasssel的博客 920

一文搞懂什么是SQL注入---SQL注入详解

文章目录一:什么是sql注入二:SQL注入攻击的总体思路三:SQL注入攻击实例四:如何防御SQL注入1、检查变量数据类型和格式2、过滤特殊符号3、绑定变量,使用预编译语句五:什么是sql预编译1.1:预编译语句是什么1.2:MySQL的预编译功能六:为什么PrepareStatement可以防止sql注入(1)为什么Statement会被sql注入(2)为什么Preparement可以防止SQL注入。 一:什么是sql注入 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而

Li_Jian_Hui_的博客 2万+
上一篇: LFI to RCE
下一篇: 无回显RCE
-ALe-
博客等级 码龄2年 530粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值