Go,Gorm 和 Mysql 是如何防止 SQL 注入的

SQL 注入和 SQL 预编译技术

什么是 SQL 注入

所谓SQL注入(sql inject),就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。

SQL 注入例子

如下所示,是一个用户进行登录时,输入用户名和密码,再将数据通过表单传送到后端进行查询的 SQL 语句。

sql = "SELECT USERNAME,PASSWORD FROM USER WHERE USERNAME='" + username + "' AND PASSWORD='" + password + "'";

上面这个 SQL 语句就存在 SQL 注入的安全漏洞。

假如 user 表中有用户名为 123456 ,密码为 123456 的记录,而在前台页面提交表单的时候用户输入的用户名和密码是随便输入的,这样当然是不能登录成功的。

但是如果后台处理的 SQL 语句是如上所写,前台页面用户名也是随便输入,而用户输入的密码是这样的 aaa' or '1'='1 ,处理登录的 SQL 语句就相当于是这样的:

SELECT USERNAME,PASSWORD FROM USER WHERE USERNAME='123456' AND PASSWORD='aaa' or '1'='1';

我们知道,1=1 是 true,所以上面这个 SQL 语句是可以执行成功的,这是一个 SQL 注入问题。

SQL 注入的解决

上述 SQL 注入问题产生的原因就是用户的输入是包含 SQL 语句的,而且后端执行 SQL 语句时直接将用户的输入和查询的 SQL 语句进行了拼接。

因此,简单的拼接用户输入的数据和后端的查询 SQL 语句,是不可行的,我们需要将用户的输入作为一个完整的字符串,而忽略内部的 SQL 语句。当用户输入的密码是这样的 aaa’ or ‘1’='1 ,处理登录的 SQL 语句实际应该执行的是:

SELECT USERNAME,PASSWORD FROM USER WHERE USERNAME='123456' AND PASSWORD="aaa' or '1'='1";

这样就可以避免 SQL 注入导致的安全漏洞。

SQL 预编译技术

解决 SQL 注入问题的这个方案的关键要点实际上是将 SQL 语句和用户输入的查询数据分别进行处理,而不是一视同仁的作为 SQL 语句的不同部分进行拼接处理。在这个基础上,就产生了 SQL 预编译技术。

通常我们的一条 SQL 在 DB 接收到最终执行完毕返回可以分为下面三个过程:

词法和语义解析

优化 SQL 语句,制定执行计划

执行并返回结果

但是我们可以将其中需要用户输入的值用占位符替代,可以视为将 SQL 语句模板化或者说参数化,再将这样的 SQL 语句进行预编译的处理,在实际运行的时候,再传入用户输入的数据。

使用这样的 SQL 预编译技术,除了可以防止 SQL 注入外,还可以对预编译的 SQL 语句进行缓存,之后的运行就省去了解析优化 SQL 语句的过程,可以加速 SQL 的查询。

Gorm 和 Go 端的 SQL 预编译

在 Gorm 中,就为我们封装了 SQL 预编译技术,可以供我们使用。

db = db.Where("merchant_id = ?", merchantId)

在执行这样的语句的时候实际上我们就用到了 SQL 预编译技术,其中预编译的 SQL 语句merchant_id = ?和 SQL 查询的数据merchantId将被分开传输至 DB 后端进行处理。

db = db.Where(fmt.Sprintf("merchant_id = %s", merchantId))

而当你使用这种写法时,即表示 SQL 由用户来进行拼装,而不使用预编译技术,随之可能带来的,就是 SQL 注入的风险。

Gorm 端的 SQL 预编译

// SQLCommon is the minimal database connection functionality gorm requires. Implemented by *sql.DB.

type SQLCommon interface {

Exec(query string, args ...interface{}) (sql.Result, error)

......

}

Go 端的 SQL 预编译

// src/database/sql/sql.go

func (db *DB) execDC(ctx context.Context, dc *driverConn, release func(error), query string, args []interface{}) (res Result, err error) {

......

resi, err = ctxDriverExec(ctx, execerCtx, execer, query, nvdargs)

......

if err != driver.ErrSkip {

......

return driverResult{dc, resi}, nil

}

......

si, err = ctxDriverPrepare(ctx, dc.ci, query)

......

ds := &driverStmt{Locker: dc, si: si}

......

return resultFromStatement(ctx, dc.ci, ds, args...)

}

实际的实现最终还是落到了go-sql-driver上,如下面代码所示go-sql-driver支持开启预编译和关闭预编译,由mc.cfg.InterpolateParams = false、true决定,可以看出gorm中mc.cfg.InterpolateParams = true,即开启了预编译

func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {

......

if len(args) != 0 {

if !mc.cfg.InterpolateParams {

return nil, driver.ErrSkip

}

prepared, err := mc.interpolateParams(query, args)

if err != nil {

return nil, err

}

query = prepared

}

......

err := mc.exec(query)

......

return nil, mc.markBadConn(err)

}

Mysql 端的SQL 预编译

在MySQL中是如何实现预编译的,MySQL在4.1后支持了预编译,其中涉及预编译的指令实例如下

可以通过PREPARE预编译指令,SET传入数据,通过EXECUTE执行命令

mysql> PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';

Query OK, 0 rows affected (0.00 sec)

Statement prepared

mysql> SET @a = 3;

Query OK, 0 rows affected (0.00 sec)

mysql> SET @b = 4;

Query OK, 0 rows affected (0.00 sec)

mysql> EXECUTE stmt1 USING @a, @b;

+------------+

| hypotenuse |

+------------+

| 5 |

+------------+

1 row in set (0.00 sec)

mysql> DEALLOCATE PREPARE stmt1;

Query OK, 0 rows affected (0.00 sec)

首先我们先简单回顾下客户端使用 Prepare 请求过程:

客户端发起 Prepare 命令将带 “?” 参数占位符的 SQL 语句发送到数据库,成功后返回 stmtID。

具体执行 SQL 时,客户端使用之前返回的 stmtID,并带上请求参数发起 Execute 命令来执行 SQL。

不再需要 Prepare 的语句时,关闭 stmtID 对应的 Prepare 语句。

这里展示不使用 sql 预编译和使用 sql 预编译时的 Mysql 的日志。

2020-06-30T08:14:02.430089Z 10 Query COMMIT

2020-06-30T08:14:02.432995Z 10 Query select * from user where merchant_id='123456'

2020-06-30T08:15:10.581287Z 12 Query COMMIT

2020-06-30T08:15:10.584109Z 12 Prepare select * from user where merchant_id =?

2020-06-30T08:15:10.584725Z 12 Execute select * from user where merchant_id ='123456'

golang mysql 防注入_Go,Gorm 和 Mysql 是如何防止 SQL 注入的相关推荐

  1. mysql sql注入漏洞修复_从Java角度修复SQL注入漏洞

    很多情况因为过滤不严导致很多网站存在sql注入,这里以用户登陆为例,简单举例 首先创建一个测试的数据库 比较基础,不写创建过程了 java代码如下: packagecn.basic.jdbc;impo ...

  2. php登录框注入,分享一个php的防火墙,拦截SQL注入和xss

    这个是一个一个基于php的防火墙程序,拦截sql注入和xss攻击,无需服务器支持 安装 composer require xielei/waf 使用说明 $waf = new \Xielei\Waf\ ...

  3. spring mysql防注入攻击_【spring】(填坑)sql注入攻击 - 持久层参数化

    结果 填坑失败,并没有看懂是如何检测sql攻击的. 只能说的是:建议都使用参数化传递sql语句参数.(所以,用hibernate.mybatis等框架的真不用太担心sql攻击问题.) 前言 在上文中的 ...

  4. mysql注入如何读取本地文件_如何通过SQL注入获取服务器本地文件

    写在前面的话 SQL注入可以称得上是最臭名昭著的安全漏洞了,而SQL注入漏洞也已经给整个网络世界造成了巨大的破坏.针对SQL漏洞,研究人员也已经开发出了多种不同的利用技术来实施攻击,包括非法访问存储在 ...

  5. mysql 注入用例_SQL注入漏洞安全测试(WVS/POST型/手工SQL注入)

    1.WVS自动化SQL注入测试 n  测试目的: 测试网站是否存在SQL注入漏洞. n  测试用例: 1)     Scan settings选择sql injection策略: 2)     输入扫 ...

  6. sql注入获取mysql版本信息_这可能是最全的SQL注入总结,很有用

    SQL注入原理 当客户端提交的数据未作处理或转义直接带入数据库,就造成了sql注入. 攻击者通过构造不同的sql语句来实现对数据库的任意操作. SQL注入的分类 按变量类型分:数字型和字符型 按HTT ...

  7. Python全栈(五)Web安全攻防之7.MySQL注入读写文件和HTTP头中的SQL注入

    文章目录 一.MySQL注入读写文件 1.搭建新的测试环境(靶场) 2.读写文件概述 3.读取文件 4.写入文件 二.HTTP头中的SQL注入 1.HTTP头中的SQL注入介绍 updatexml函数 ...

  8. mysql数据库蛛_超级蜘蛛池之何谓为SQL注入?【基础】

    什么是SQL注入? 还记得小学语文考试上的填空题吗? 题目的意图明显是通过填空来了解答题者的名字和爱好. 比如:我是_______________,喜欢__________________ 如果有同学 ...

  9. MySQL注入读写文件、HTTP头中的SQL注入和cookie注入

    MySQL注入读写文件 MySQL数据库在渗透过程中能够使用的功能还是比较多的,除了读取数据之外,还可以进行对文件进行读写(前提是权限足够) 读取前提: 1.用户权限足够高,尽量具有root权限 2. ...

  10. MySQL JDBC常用知识,封装工具类,时区问题配置,SQL注入问题

    JDBC JDBC介绍 Sun公司为了简化开发人员的(对数据库的统一)操作,提供了(Java操作数据库的)规范,俗称JDBC,这些规范的由具体由具体的厂商去做 对于开发人员来说,我们只需要掌握JDBC ...

最新文章

  1. 全球半导体产业迁移 中国的机遇与挑战
  2. 添加全局函数$.extend和对象方法$.fn
  3. PAT甲级1142 Maximal Clique :[C++题解]图论、最大团、枚举
  4. SAP CRM Collection wrapper的publish_current用法
  5. 深入浅出 Spring
  6. 广度优先搜索——USACO08FEB(洛谷 P2895)
  7. Spring/Spring boot正确集成Quartz及解决@Autowired失效问题
  8. jquery ajax 请求中多出现一次OPTIONS请求及其解决办法
  9. PIFA 天线的推论及计算方法
  10. win版跳过id锁工具_实测:一键跳过苹果锁,免费
  11. 【2022最新Java面试宝典】—— Linux面试题(50道含答案)
  12. oracle日文编码格式,php – 如何在oracle数据库中正确显示日文字符
  13. Android Studio 如何快速把一个类中的所有同一个变量名统一改(同时工程项目里的文件名被改)
  14. c语言数学函数指数,C语言数学函数参考表
  15. go标准库的中文翻译
  16. 代码解读六 文件名“Ano_AltCtrl.c”
  17. 摘录互联网企业的优秀企业文化集萃
  18. STM32CubeMX和keil实现led灯的点亮
  19. SQL SERVER练习题及答案2
  20. 2021.09.27-10.3 AI行业周刊(第65期):坚持的力量

热门文章

  1. Google上面关于cas的文章
  2. Dapr项目应用探索
  3. C# WPF MVVM模式下在主窗体显示子窗体并获取结果
  4. gRPC在C#中的未来属于grpc-dotnet
  5. dotnet core TargetFramework 解析顺序探索
  6. .net 5.0 中的 JsonConsole
  7. 一个基于.Net 5开发的轻量级Quartz配置中心 - QuartzCore.Blazor
  8. 注意| .NET开发者大会防疫须知 !
  9. 对 精致码农大佬 说的 Task.Run 会存在 内存泄漏 的思考
  10. [Mvp.Blazor] 集成Ids4,实现统一授权认证