一次sql请求,返回分页数据和总条数

06822c1b706026f08a919abf1655d518.gif

日常搬砖,总少不了需要获取分页数据和总行数

一直以来的实践是编码两次sql请求,分别拉分页数据和totalCount。

最近我在思考:

常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新,显而易见的优势:

① 能显著减低“客户端和服务器之间的网络往返次数”,提高吞吐量
② 简化客户端代码逻辑


1. mysql 默认单sql请求单语句

mysql客户端选项client_multi_statements默认为false:会禁止多条 SQL 语句的执行,这意味着在单个sql请求中只有第一条 SQL 语句会被执行,后续的 SQL 语句将被忽略。

这是一种提高数据库操作安全性的方法,可以有效防止 SQL 注入攻击和意外执行多条语句带来的风险。

MySQL客户端支持修改这样的设定 :client_multi_statements=true。

5b12b1fea06772519f8057bfb6eab25d.png

劣势:存在sql注入的风险, 错误处理比较复杂。

(1) go-sql-driver开启多语句支持: multiStatements=true

(2)

SELECT *  FROM `dict_plugin`  limit  20 ,10;
SELECT count(*) as  totalCount  from `dict_plugin`;

将会形成2个数据集,golang的实践如下:

results, err = p.Query(querystring)for results.Next() {err = results.Scan(&...)}if !results.NextResultSet() {log.ErrorF(ctx, "expected more result sets: %v", results.Err())}for results.Next() {err = results.Scan(&totalCount)}

既然提到了开启client_multi_statements 有sql注入的风险,我们就展开聊一聊。

2. sql注入

我们先看下sql注入的原理:

有这样的业务sql:

var input_name string
query: = "select  * from user where user_name='" + input_name+"'"
sql.Query(query)

如果从界面输入的input_name="janus';delete from user;  --",
会形成恶意sql:select * from user where user_name='janus';delete from user;  --' 。

这个时候,客户端的client_multi_statements默认值为false就能于水火之间挽救数据库:执行第一个sql之后,后面的恶意sql都不会执行。

由此可知,client_multi_statements=false,确实可以显著降低sql注入的风险,但是还是没有办法避免单sql注入, 比如从界面密码框注入' OR '1'='1 会绕过登录认证。

query:= "select * from user where user='" + input_name +"' and  pwd='" +input_pwd +"'" select * from user where user='xxx' and pwd='' OR '1'='1'  -- 会绕过认证逻辑。

3. 参数化查询防止sql注入

参数化查询可以防止sql注入风险[1]

// Correct format for executing an SQL statement with parameters.var queryStr = "SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?"
var args string = "55 union select * from `dict_plugin_Test`"rows, err := db.Query(queryStr, args)

sql查询内部会利用提供的参数1创建预编译语句, 在运行时,实际是执行带参的预编译后的语句。

在服务器收到的查询日志如下:

2024-08-13T08:07:18.922818Z   26 Connect root@localhost on tcinfra_janus_sharing using TCP/IP
2024-08-13T08:07:18.924525Z   26 Prepare SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?
2024-08-13T08:07:18.924671Z   26 Execute SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = '55 union select * from `dict_plugin_Test`'
2024-08-13T08:07:18.925273Z   26 Close stmt

判断mysql数据库开启了查询日志:show variables like '%general_log%';
打开sql查询日志的开关:set global general_log = on; 。

注意:参数占位符根据DBSM和驱动而有所不同,例如,Postgres 的pq驱动程序接受占位符形式是 $1而不是?。

3.1 预编译语句

数据库预编译后, SQL语义结构和数据分离,这样即使输入包含恶意代码,它也只会被当作数据处理,不会影响已经被解析固定的SQL语义结构。

预编译语句包含两次 sql交互:

①  预编译阶段(Prepare Phase):

  • 客户端向服务器发送一个包含 SQL 语句(带有参数占位符)的请求。

  • sql服务器对SQL 语句进行语法和语义检查,然后对其进行预编译,并为其分配一个标识符(Statement ID)。

  • 服务器返回一个确认响应,表示预编译语句已经成功准备好。

②  执行阶段(Execute Phase):

  • 客户端发送执行请求,包含预编译语句的标识符和实际参数值。

  • 服务器将参数值绑定到预编译语句的占位符上,然后执行该语句。

  • 服务器返回执行结果(如结果集或影响的行数)。

图示如下:

客户端                          服务器|                               ||----预编译语句(Prepare)------>||                               ||<-------确认响应(OK)----------||                               ||---执行语句(Execute) + 参数---->||                               ||<----------查询结果-------------|

我们了解到预编译语句,将SQL语义和数据分离,通过两次sql交互(在预编译阶段固定了sql语义结构), 有效防止了SQL注入攻击, 另一方面,预编译语句在重复执行某一sql语句时确实有加快查询结果的效果。

golang的预编译的写法与常规的sql查询类似:

stmt, err := p.Prepare("SELECT * FROM `dict_plugin_Test` WHERE `plugin_name` = ?")
var args string = "55 union select * from `dict_plugin_Test`"
results, err := stmt.Query(args)
if err != nil {fmt.Printf("query fail: %v", err)return err
}
defer stmt.Close()for results.Next() {err = results.Scan(.....)......
}

btw, C#  其实也支持预编译语句版本的sqlCommand:SqlCommand.Prepare()

总结

本文通过我们最初开始数据库编程时的一个实践, 提出在【一次sql请求中执行多次sql查询】的猜想;

了解到client_multi_statements= false 确实能避免一部分sql注入风险;

之后落地到sql注入的原理, 给出了参数化查询(预编译语句)能防止sql注入的核心机制。

参考资料

[1]

参数化查询可以防止sql注入风险: https://go.dev/doc/database/sql-injection

8d06b98bd82aa73ca9652fba819f28b0.gif

自古以来,同步/异步都是八股文第一章

async/await 贴脸输出,这次你总该明白了

流量调度、微服务可寻址性和注册中心

Go语言正/反向代理的姿势

两将军问题和TCP三次握手

"家长进校园"之《计算机和人工智能》

903d18c5a32d92d0ca579dc91845b98f.png

点“5188979d1bab0f1d4604485dca023c3b.gif戳“在看545b5850adc9ae8bc1ff113e573a0446.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/401322.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Halcon 算子汇总

gen_tuple_const(1000,1.5) 生成一个长度为1000&#xff0c;里面每一个数组元素都为1.5的数组 gen_tuple_const(100,chr(ord(a) 1)) 生成一个长度为100&#xff0c;里面每一个数组元素都为b的数组 ord函数是库函数&#xff0c;用于获取字符的ASCII值 chr(ord(a) 1) 结…

8.13-LVS的nat模式+DR模式

LVS 一、nat模式 1.角色 主机名ip地址功能web01192.168.2.101rsweb02192.168.2.102realserveenat内网:192.168.2.103 外网:192.168.2.120directorserver,ntpdns192.168.2.105dns 2..web服务器 [rootweb01 ~]# yum -y install nginx ​ [rootweb01 ~]# echo "web01&qu…

【14】二叉树的Morris等

目录 一.树形dp套路 二.派对的最大快乐值 三.Morris遍历 morris先序遍历 morris中序遍历 moris后序遍历 判断是不是搜索二叉树 四.额外习题 一.树形dp套路 情况1&#xff1a;最大距离&#xff0c;节点X不参与。 > 左树最大距离 or 右树最大距离 情况2&#xff1a;最…

html编写贪吃蛇页面小游戏(可以玩)

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>贪吃蛇小游戏</title><style>body {…

【软件逆向】第2课,软件逆向安全工程师之区分应用32位和64位,每天5分钟学习逆向吧!

目标学习使用StudyPE区分应用 在软件逆向中区分应用类型是关键性的一部分 &#xff0c;只有区分类型后才能选择对应工具进行后续处理。 1.打开StudyPE工具。 2.将我们需要逆向的软件&#xff0c;拖拽到StudyPE中&#xff0c;查看应用信息。 以上用一款视觉AI软件举例&#…

Java设计模式-原型模式-一次性理解透

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1. 前言2. 原型模式的主要角色2.1 原型接口或抽象类2.2 具体原型类2.3 客户端2.4 克隆方法 3. 原型模式使用场景3.1 创建对象是昂贵的3.2 对象的变化3.3 动态配置3.…

【STM32】DMA数据转运(存储器到存储器)

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 DMA简介 DMA时钟使能 DMA初始化 转运起始和终止的地址 转运方向 数据宽度 传输次数 转运触发方式 转运模式 通道优先级 DMA初始化框架 选择开启DMA通道 更改转运次数 DMA应用…

【第二节】80x86汇编-寄存器和标志位

目录 前言 一、汇编相关概念 1.1 数据表示与类型 1.2 汇编语言的构成 1.3 存储器及指令、数据 1.4 存储单元 1.5 CPU对存储器的读写操作 1.6 CPU读写内存单元的过程 1.7 intel CPU发展 1.8 8086 内部结构 二、寄存器 2.1 寄存器概览 2.2 32位寄存器 2.3 16位寄存器…

三维建模软件:地理信息与遥感领域的智慧构建者

在地理信息与遥感技术的广阔舞台中&#xff0c;建模软件如同一位卓越的建筑师&#xff0c;以数据为砖瓦&#xff0c;智慧为水泥&#xff0c;构建出一个又一个又一个逼真、动态的虚拟世界。本文将深入探究其技术核心、应用实例、未来趋势&#xff0c;揭示建模软件如何在地理信息…

《爱情,到此为止》票房大卖 贾斯汀巴尔多尼与布莱克莱弗利的矛盾升级 是真的还是炒作

布蕾克莱弗利&#xff0c;贾斯汀巴尔多尼 布莱克莱弗利凭借电影《我们的末日》在周末取得了票房成功&#xff0c;首映票房收入达 5000 万美元。在电影院困难时期&#xff0c;这是一个了不起的成就&#xff0c;但没有人谈论这一胜利——粉丝们对她与导演兼联合主演贾斯汀巴尔多…

排序(基数,堆,归并)

基数排序 定义0-9十个桶&#xff0c;先排序个数&#xff0c;在排序十位&#xff0c;依次向下&#xff08;桶就是二维数组&#xff09; 按照个位先排一次 个位已经有序了&#xff0c;桶内遵循先进先出 没有十位放到0里 取出 百位 这样排序就完成了。放进取出几次&#xff0c;取…

Flink Checkpoint expired before completing解决方法

在Flink消费Kafka日志的时候出现了这样的一则报错&#xff0c; JobManager报错如下&#xff1a; 2024-03-07 15:21:12,500 [Checkpoint Timer] WARN org.apache.flink.runtime.checkpoint.CheckpointFailureManager [] - Failed to trigger or complete checkpoint 181 for …

Python酷库之旅-第三方库Pandas(082)

目录 一、用法精讲 341、pandas.Series.str.startswith方法 341-1、语法 341-2、参数 341-3、功能 341-4、返回值 341-5、说明 341-6、用法 341-6-1、数据准备 341-6-2、代码示例 341-6-3、结果输出 342、pandas.Series.str.strip方法 342-1、语法 342-2、参数 …

bug的常见排查和分析思路以及相关的原因分类

作为开发人员&#xff0c;经常会收到来自用户和QA&#xff0c;领导反馈的各种问题。 为了快速问题&#xff0c;我们有时需要站在更高的角度&#xff0c;更全面的看待问题。才能更快锁定问题。 具体的bug还需要结合企业实际业务情况&#xff0c;相关的框架&#xff0c;依赖库&…

PHP项目任务系统小程序源码

&#x1f680;解锁高效新境界&#xff01;我的项目任务系统大揭秘&#x1f50d; &#x1f31f; 段落一&#xff1a;引言 - 为什么需要项目任务系统&#xff1f; Hey小伙伴们&#xff01;你是否曾为了杂乱的待办事项焦头烂额&#xff1f;&#x1f92f; 或是项目截止日逼近&…

QT、C++简单界面设计

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {---------------------窗口设置----------------------this->setWindowTitle("南城贤子摄影工作室");//设置窗口标题this->setWindowIcon(QIcon("d:\\Pictures\\C…

PUMA论文阅读

PUMA: Efficient Continual Graph Learning with Graph Condensation PUMA&#xff1a;通过图压缩进行高效的连续图学习 ABSTRACT 在处理流图时&#xff0c;现有的图表示学习模型会遇到灾难性的遗忘问题&#xff0c;当使用新传入的图进行学习时&#xff0c;先前学习的这些模…

c语言中比较特殊的输入格式

目录 一.%[ ] 格式说明符 1.基本用法 (1)读取字母字符: (2)读取数字字符: (3)读取所有字符直到遇到空格: (4)读取直到换行符: 2.使用范围和组合: 3.^ 取反操作 4.注意事项 (1). 字符范围的正确表示 (2). 避免字符集中的特殊字符冲突 (3).避免空字符集 (4). 输入长…

构建高效外贸电商系统的技术探索与源码开发

在当今全球化的经济浪潮中&#xff0c;外贸电商作为连接国内外市场的桥梁&#xff0c;其重要性日益凸显。一个高效、稳定、功能全面的外贸电商系统&#xff0c;不仅能够助力企业突破地域限制&#xff0c;拓宽销售渠道&#xff0c;还能提升客户体验&#xff0c;增强品牌竞争力。…

Wireshark过滤规则

一、按IP地址过滤 1、查看源IP为 xx 的包 ip.srcIP地址 例如&#xff1a;ip.src172.18.10.56 2、查看目标IP为 xx 的包 ip.dstIP地址 例如&#xff1a;ip.dst172.16.76.251 3、查看源或目标IP为 xx 的包 ip.addrIP地址 例如&#xff1a;ip.addr172.18.10.56 二、按MAC地…