避免SQL注入风险

本文翻译自《Avoiding SQL injection risk》。

你可以通过提供SQL参数值作为sql包里函数的参数来避免SQL注入风险。sql包中的许多函数为SQL语句(包括准备语句)的参数提供参数值。

以下示例中的代码使用?作为SQL语句的参数的占位符,由Query函数的id参数提供值:

// 使用参数执行一个SQL语句的正确格式
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)

执行数据库操作的sql包的函数根据你提供的参数创建SQL准备语句。在运行时,sql包将SQL语句转换为准备语句并将其与参数分开发送。

注意:参数占位符因你使用的DBMS和驱动程序而异。例如,Postgres的pq驱动程序使用$1形式的占位符形式,而不是?号。

你可能想使用fmt包中的函数将SQL语句组装为包含参数值的字符串——如下所示:

// 有安全风险!
rows, err := db.Query(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))

这不安全!执行此操作时,Go会组装整个SQL语句,在将完整语句发送到DBMS之前用参数值替换%s格式动词。这会带来SQL注入风险,因为代码的调用者可能会发送意外的SQL片段作为id参数。该片段可能会以不可预测的方式执行SQL语句,这对你的应用程序是危险的。

例如,通过传递一个特定的%s值,你可能会得到类似于以下内容的SQL语句,它可能会返回数据库中的所有用户记录:

SELECT * FROM user WHERE id = 1 OR 1=1;