日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

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

發(fā)布時(shí)間:2023/12/4 数据库 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 golang mysql 防注入_Go,Gorm 和 Mysql 是如何防止 SQL 注入的 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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

SQL 注入和 SQL 預(yù)編譯技術(shù)

什么是 SQL 注入

所謂SQL注入(sql inject),就是通過(guò)把SQL命令插入到Web表單提交或輸入域名或頁(yè)面請(qǐng)求的查詢字符串,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令。具體來(lái)說(shuō),它是利用現(xiàn)有應(yīng)用程序,將(惡意的)SQL命令注入到后臺(tái)數(shù)據(jù)庫(kù)引擎執(zhí)行的能力,它可以通過(guò)在Web表單中輸入(惡意)SQL語(yǔ)句得到一個(gè)存在安全漏洞的網(wǎng)站上的數(shù)據(jù)庫(kù),而不是按照設(shè)計(jì)者意圖去執(zhí)行SQL語(yǔ)句。

SQL 注入例子

如下所示,是一個(gè)用戶進(jìn)行登錄時(shí),輸入用戶名和密碼,再將數(shù)據(jù)通過(guò)表單傳送到后端進(jìn)行查詢的 SQL 語(yǔ)句。

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

上面這個(gè) SQL 語(yǔ)句就存在 SQL 注入的安全漏洞。

假如 user 表中有用戶名為 123456 ,密碼為 123456 的記錄,而在前臺(tái)頁(yè)面提交表單的時(shí)候用戶輸入的用戶名和密碼是隨便輸入的,這樣當(dāng)然是不能登錄成功的。

但是如果后臺(tái)處理的 SQL 語(yǔ)句是如上所寫(xiě),前臺(tái)頁(yè)面用戶名也是隨便輸入,而用戶輸入的密碼是這樣的 aaa' or '1'='1 ,處理登錄的 SQL 語(yǔ)句就相當(dāng)于是這樣的:

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

我們知道,1=1 是 true,所以上面這個(gè) SQL 語(yǔ)句是可以執(zhí)行成功的,這是一個(gè) SQL 注入問(wèn)題。

SQL 注入的解決

上述 SQL 注入問(wèn)題產(chǎn)生的原因就是用戶的輸入是包含 SQL 語(yǔ)句的,而且后端執(zhí)行 SQL 語(yǔ)句時(shí)直接將用戶的輸入和查詢的 SQL 語(yǔ)句進(jìn)行了拼接。

因此,簡(jiǎn)單的拼接用戶輸入的數(shù)據(jù)和后端的查詢 SQL 語(yǔ)句,是不可行的,我們需要將用戶的輸入作為一個(gè)完整的字符串,而忽略內(nèi)部的 SQL 語(yǔ)句。當(dāng)用戶輸入的密碼是這樣的 aaa’ or ‘1’='1 ,處理登錄的 SQL 語(yǔ)句實(shí)際應(yīng)該執(zhí)行的是:

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

這樣就可以避免 SQL 注入導(dǎo)致的安全漏洞。

SQL 預(yù)編譯技術(shù)

解決 SQL 注入問(wèn)題的這個(gè)方案的關(guān)鍵要點(diǎn)實(shí)際上是將 SQL 語(yǔ)句和用戶輸入的查詢數(shù)據(jù)分別進(jìn)行處理,而不是一視同仁的作為 SQL 語(yǔ)句的不同部分進(jìn)行拼接處理。在這個(gè)基礎(chǔ)上,就產(chǎn)生了 SQL 預(yù)編譯技術(shù)。

通常我們的一條 SQL 在 DB 接收到最終執(zhí)行完畢返回可以分為下面三個(gè)過(guò)程:

詞法和語(yǔ)義解析

優(yōu)化 SQL 語(yǔ)句,制定執(zhí)行計(jì)劃

執(zhí)行并返回結(jié)果

但是我們可以將其中需要用戶輸入的值用占位符替代,可以視為將 SQL 語(yǔ)句模板化或者說(shuō)參數(shù)化,再將這樣的 SQL 語(yǔ)句進(jìn)行預(yù)編譯的處理,在實(shí)際運(yùn)行的時(shí)候,再傳入用戶輸入的數(shù)據(jù)。

使用這樣的 SQL 預(yù)編譯技術(shù),除了可以防止 SQL 注入外,還可以對(duì)預(yù)編譯的 SQL 語(yǔ)句進(jìn)行緩存,之后的運(yùn)行就省去了解析優(yōu)化 SQL 語(yǔ)句的過(guò)程,可以加速 SQL 的查詢。

Gorm 和 Go 端的 SQL 預(yù)編譯

在 Gorm 中,就為我們封裝了 SQL 預(yù)編譯技術(shù),可以供我們使用。

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

在執(zhí)行這樣的語(yǔ)句的時(shí)候?qū)嶋H上我們就用到了 SQL 預(yù)編譯技術(shù),其中預(yù)編譯的 SQL 語(yǔ)句merchant_id = ?和 SQL 查詢的數(shù)據(jù)merchantId將被分開(kāi)傳輸至 DB 后端進(jìn)行處理。

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

而當(dāng)你使用這種寫(xiě)法時(shí),即表示 SQL 由用戶來(lái)進(jìn)行拼裝,而不使用預(yù)編譯技術(shù),隨之可能帶來(lái)的,就是 SQL 注入的風(fēng)險(xiǎn)。

Gorm 端的 SQL 預(yù)編譯

// 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 預(yù)編譯

// 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...)

}

實(shí)際的實(shí)現(xiàn)最終還是落到了go-sql-driver上,如下面代碼所示go-sql-driver支持開(kāi)啟預(yù)編譯和關(guān)閉預(yù)編譯,由mc.cfg.InterpolateParams = false、true決定,可以看出gorm中mc.cfg.InterpolateParams = true,即開(kāi)啟了預(yù)編譯

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 預(yù)編譯

在MySQL中是如何實(shí)現(xiàn)預(yù)編譯的,MySQL在4.1后支持了預(yù)編譯,其中涉及預(yù)編譯的指令實(shí)例如下

可以通過(guò)PREPARE預(yù)編譯指令,SET傳入數(shù)據(jù),通過(guò)EXECUTE執(zhí)行命令

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)

首先我們先簡(jiǎn)單回顧下客戶端使用 Prepare 請(qǐng)求過(guò)程:

客戶端發(fā)起 Prepare 命令將帶 “?” 參數(shù)占位符的 SQL 語(yǔ)句發(fā)送到數(shù)據(jù)庫(kù),成功后返回 stmtID。

具體執(zhí)行 SQL 時(shí),客戶端使用之前返回的 stmtID,并帶上請(qǐng)求參數(shù)發(fā)起 Execute 命令來(lái)執(zhí)行 SQL。

不再需要 Prepare 的語(yǔ)句時(shí),關(guān)閉 stmtID 對(duì)應(yīng)的 Prepare 語(yǔ)句。

這里展示不使用 sql 預(yù)編譯和使用 sql 預(yù)編譯時(shí)的 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'

總結(jié)

以上是生活随笔為你收集整理的golang mysql 防注入_Go,Gorm 和 Mysql 是如何防止 SQL 注入的的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。