朋友说他们公司近期发现一些SQL注入问题,究其原因还是因为代码中使用了拼接查询,没有使用参数化查询,而且这种历史遗留问题较难梳理,可能很多都是3-5年前的代码,于是和我了解一种批量白盒审计SQL注入的方法。
通过gosec扫描代码中的注入问题:
gosec 是一个静态分析工具,用于扫描 Go 代码以查找潜在的安全问题。它可以识别常见的代码漏洞、敏感信息泄露和其他安全问题,帮助开发人员提前发现并修复潜在的安全隐患。
package main
import (
"database/sql"
"fmt"
"net/http"
"os"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func main() {
db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/mydb")
if err != nil {
fmt.Println("Failed to connect to the database:", err)
os.Exit(1)
}
defer db.Close()
http.HandleFunc("/search", searchHandler)
http.ListenAndServe(":8080", nil)
}
func searchHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("query")
if query == "" {
fmt.Fprint(w, "Please provide a search query.")
return
}
// Potential SQL injection vulnerability using fmt.Sprintf with %s
sqlQuery := fmt.Sprintf("SELECT name, age FROM users WHERE name = '%s'", query)
rows, err := db.Query(sqlQuery)
if err != nil {
fmt.Fprint(w, "An error occurred.")
return
}
defer rows.Close()
for rows.Next() {
var name string
var age int
if err := rows.Scan(&name, &age); err != nil {
fmt.Fprint(w, "An error occurred.")
return
}
fmt.Fprintf(w, "Name: %s, Age: %d\n", name, age)
}
}
扫描结果:
osec ./...
[gosec] 2023/10/30 10:06:45 Including rules: default
[gosec] 2023/10/30 10:06:45 Excluding rules: default
[gosec] 2023/10/30 10:06:45 Import directory: /xxx/tools/test
[gosec] 2023/10/30 10:06:45 Checking package: main
[gosec] 2023/10/30 10:06:45 Checking file: /xxx/tools/test/main.go
Results:
Golang errors in file: [/xxx/tools/test/main.go]:
> [line 9 : column 4] - could not import github.com/go-sql-driver/mysql (invalid package name: "")
[/xxx/tools/test/main.go:23] - G114 (CWE-676): Use of net/http serve function that has no support for setting timeouts (Confidence: HIGH, Severity: MEDIUM)
22: http.HandleFunc("/search", searchHandler)
> 23: http.ListenAndServe(":8080", nil)
24: }
[/xxx/tools/test/main.go:34] - G201 (CWE-89): SQL string formatting (Confidence: HIGH, Severity: MEDIUM)
33: // Potential SQL injection vulnerability using fmt.Sprintf with %s
> 34: sqlQuery := fmt.Sprintf("SELECT name, age FROM users WHERE name = '%s'", query)
35: rows, err := db.Query(sqlQuery)
[/xxx/tools/test/main.go:23] - G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)
22: http.HandleFunc("/search", searchHandler)
> 23: http.ListenAndServe(":8080", nil)
24: }
Summary:
Gosec : dev
Files : 1
Lines : 51
Nosec : 0
Issues : 3
包导入错误:
[line 9 : column 4] - 无法导入 github.com/go-sql-driver/mysql,因为它的包名为空。这个问题是由于在代码中试图导入一个没有包名的包而引发的。你需要检查你的导入语句并确保包名正确。
HTTP服务器超时设置问题:
[/xxx/tools/test/main.go:23] - G114 (CWE-676): 使用net/http的serve函数,它不支持设置超时时间。这表示你的HTTP服务器在处理请求时没有设置超时,这可能导致潜在的性能和安全问题。
SQL字符串格式化问题:
[/xxx/tools/test/main.go:34] - G201 (CWE-89): SQL字符串格式化。你使用了fmt.Sprintf来构建SQL查询字符串,这会导致潜在的SQL注入漏洞。最好使用参数化查询或者正确转义输入来避免这种问题。
未处理错误:
[/xxx/tools/test/main.go:23] - G104 (CWE-703): 未处理错误。在HTTP服务器的启动代码中,错误没有被处理。你应该考虑捕获错误并采取适当的措施来处理它们,以提高代码的健壮性。
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"strings"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func main() {
// 连接到数据库
var err error
db, err = sql.Open("mysql", "username:password@tcp(localhost:3306)/mydatabase")
if err != nil {
log.Fatal(err)
}
defer db.Close()
http.HandleFunc("/search", searchHandler)
http.ListenAndServe(":8080", nil)
}
func searchHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("query")
// 构建SQL查询字符串(易受SQL注入攻击)
sqlQuery := "SELECT name, age FROM users WHERE name = '" + query + "'"
rows, err := db.Query(sqlQuery)
if err != nil {
log.Println("Error executing query:", err)
return
}
defer rows.Close()
var name string
var age int
for rows.Next() {
err := rows.Scan(&name, &age)
if err != nil {
log.Println("Error scanning row:", err)
continue
}
fmt.Fprintf(w, "Name: %s, Age: %d\n", name, age)
}
}
扫描结果:
gosec ./... 1 ↵
[gosec] 2023/10/30 10:26:49 Including rules: default
[gosec] 2023/10/30 10:26:49 Excluding rules: default
[gosec] 2023/10/30 10:26:49 Import directory: /xxx/tools/test
[gosec] 2023/10/30 10:26:50 Checking package: main
[gosec] 2023/10/30 10:26:50 Checking file: /xxx/tools/test/main.go
Results:
Golang errors in file: [/xxx/tools/test/main.go]:
> [line 9 : column 4] - could not import github.com/go-sql-driver/mysql (invalid package name: "")
[/xxx/tools/test/main.go:24] - G114 (CWE-676): Use of net/http serve function that has no support for setting timeouts (Confidence: HIGH, Severity: MEDIUM)
23: http.HandleFunc("/search", searchHandler)
> 24: http.ListenAndServe(":8080", nil)
25: }
[/xxx/tools/test/main.go:31] - G202 (CWE-89): SQL string concatenation (Confidence: HIGH, Severity: MEDIUM)
30: // 构建SQL查询字符串(易受SQL注入攻击)
> 31: sqlQuery := "SELECT name, age FROM users WHERE name = '" + query + "'"
32:
[/xxx/tools/test/main.go:24] - G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)
23: http.HandleFunc("/search", searchHandler)
> 24: http.ListenAndServe(":8080", nil)
25: }
Summary:
Gosec : dev
Files : 1
Lines : 50
Nosec : 0
Issues : 3
包导入错误:
[line 9 : column 4] - 无法导入 github.com/go-sql-driver/mysql,因为它的包名为空。这个问题是由于在代码中试图导入一个没有包名的包而引发的。你需要检查你的导入语句并确保包名正确。
HTTP服务器超时设置问题:
[/xxx/tools/test/main.go:24] - G114 (CWE-676): 使用net/http的serve函数,它不支持设置超时时间。这表示你的HTTP服务器在处理请求时没有设置超时,这可能导致潜在的性能和安全问题。
SQL字符串拼接问题:
[/xxx/tools/test/main.go:31] - G202 (CWE-89): SQL字符串拼接。在构建SQL查询字符串时,你直接将用户输入 query 字符串与其他SQL代码拼接在一起,这容易受到SQL注入攻击。这是一个严重的安全问题,应该避免使用字符串拼接构建SQL查询。
未处理错误:
[/xxx/tools/test/main.go:24] - G104 (CWE-703): 未处理错误。在HTTP服务器的启动代码中,错误没有被处理。你应该考虑捕获错误并采取适当的措施来处理它们,以提高代码的健壮性。
gosec目前通过静态扫描方式,基本满足现有SQL注入场景的检测需求。