通过gosec白盒扫描Go代码中的SQL注入

背景

朋友说他们公司近期发现一些SQL注入问题,究其原因还是因为代码中使用了拼接查询,没有使用参数化查询,而且这种历史遗留问题较难梳理,可能很多都是3-5年前的代码,于是和我了解一种批量白盒审计SQL注入的方法。

方案

通过gosec扫描代码中的注入问题:
gosec 是一个静态分析工具,用于扫描 Go 代码以查找潜在的安全问题。它可以识别常见的代码漏洞、敏感信息泄露和其他安全问题,帮助开发人员提前发现并修复潜在的安全隐患。

检测效果

问题代码1(通过%s进行拼接查询) - 可检测
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服务器的启动代码中,错误没有被处理。你应该考虑捕获错误并采取适当的措施来处理它们,以提高代码的健壮性。
问题代码2(通过“ +”进行拼接查询) - 可检测
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注入场景的检测需求。

你可能感兴趣的:(golang,sql,开发语言,web安全)