避免SQL注入风险

您可以通过将SQL参数值作为sql包函数参数提供来避免SQL注入风险。sql包中的许多函数都为SQL语句及其参数中使用的值提供了参数(其他函数则为预处理语句和参数提供参数)。

以下示例中的代码使用?符号作为id参数的占位符,该参数作为函数参数提供。

// Correct format for executing an SQL statement with parameters.
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)

执行数据库操作的sql包函数会根据您提供的参数创建预处理语句。在运行时,sql包会将SQL语句转换为预处理语句,并将其与单独的参数一起发送。

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

您可能会想使用fmt包中的函数来将SQL语句组装成一个包含参数的字符串——像这样:

// SECURITY RISK!
rows, err := db.Query(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))

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

例如,通过传递特定的%s值,您最终可能会得到类似以下的内容,这可能会返回数据库中的所有用户记录:

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