避免SQL注入风险

Avoiding SQL injection risk - 避免SQL注入风险

原文:https://go.dev/doc/database/sql-injection

​ 您可以通过提供SQL参数值作为sql包的函数实参来避免SQL注入风险。sql包中的许多函数为SQL语句和用于该语句的参数值提供了参数(其他函数为预处理语句和参数提供一个参数)。

​ 下面的例子中的代码使用? 符号作为id参数的占位符,该参数是作为函数实参提供的:

1
2
// Correct format for executing an SQL statement with parameters. => 执行带参数的SQL语句的正确格式。
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)

​ 执行数据库操作的sql包函数从您提供的实参中创建预处理语句。在运行时,sql包将SQL语句变成一个预处理语句,并将其与独立的参数一起发送。

注意:参数占位符因您所使用的DBMS和驱动而不同。例如,Postgrespq driver接受$1这样的占位符形式,而不是 ?

​ 您可能会想使用fmt包中的一个函数来把SQL语句组合成一个包含参数的字符串——比如这样:

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

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

​ 例如,通过传递某个%s值,您可能会得到如下语句,这可能会返回您数据库中的所有用户记录:

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