Golang MySQL 驱动中的 Prepare 语句(防 SQL 注入)

Go

go-sql-driver/mysql 有两个函数 Query() 和 Exec().
我想看看 Query() 是如何工作的.

两种模式

Query(query string, args ...interface{}) 函数根据是否存在 args 参数有两种模式.

纯文本模式

如果在没有 args 的情况下调用 Query(query), 我将其称为 纯文本模式.

在此模式中, 驱动程序不对查询字符串进行任何操作, 而是直接将其发送到 MySQL 服务器.

插值模式

如果查询字符串中有一些占位符 (例如 MySQL 中的 ?) 并传入了一些 args 进行插值, 我会调用 '插值模式'.

在此模式中, 驱动程序实际执行 3 个动作

  • 准备一个语句.
  • 使用给定的 args 执行准备好的语句.
  • 关闭准备好的语句.

正如预备语句的口号: Prepare Once, Execute Many.

区别

SQL 注入

假设你有一张叫做 prepare 的表

id name
1 name1
2 name2
3 name3

可以在该表上运行这样一个 SQL select id, name from prepare where id = 1;.

其返回

id name
1 name1

还不错. 好的, 接下来我们看看如何在前面说的两种模式下实现它.

  • 纯文本模式
func plaintextQuery(db *sql.DB, id string) *sql.Row {
    return db.Query("select id, name from prepare where id = " + id + ";")
}
  • 插值模式
func interpolationQuery(db *sql.DB, id string) *sql.Row {
    return db.Query("select id, name from prepare where id = ?;", id)
}

当你将 "1" 作为 id 传递时, 一切都会正常. 但是, 真的没问题嘛? 我们来尝试一个 SQL 注入案例, 将 "1 or 1 = 1" 作为 id 传递.

糟糕, interpolationQuery() 仍然返回了一样的内容, 但是 plaintextQuery() 返回了表中的所有数据, 这意味着被污染的 SQL 已经被注入了.

性能

我分别用两种模式做了一个简单的插入 SQL.

Inserts Number:  100000
Plaintext Mode
Duration 16.058662357s
Interpolation Mode
Duration 24.076297264s

这意味着 纯文本模式插值模式 有着更好的性能.
这是合理的, 因为 插值模式 下的每个 Query()Exec() 必须执行 3 次网络通信.

结论

  • 插值模式 可用于规避大多数 SQL 注入, 这是非常重要的一点. 因此, 强烈建议使用它来规避用户输入参数中可能的 SQL 注入.
  • 纯文本模式 在某种程序上具备更好的性能. 但是, 也有一些方法可以加快 插值模式, 我将在后续讨论.
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://ceshihao.github.io/2018/01/11/pr...

译文地址:https://learnku.com/go/t/49692

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!