Golang中如何判断查询语句是否安全

Golang中如何判断查询语句是否安全 我想使用一个“动态”列来选择翻译。这样做安全吗?

import (
    "context"
    "github.com/jackc/pgx/v4/pgxpool"
)

func main() {
    language := "en_US"
    translationKey := "co"

    // 创建一个数据库连接池
    dbpool, err := pgxpool.Connect(context.Background(), "postgres://user:password@host:port/database")
    if err != nil {
        panic(err)
    }
    defer dbpool.Close()

    // 使用参数占位符准备查询语句
    stmt, err := dbpool.Prepare(context.Background(), "SELECT $1 FROM lang WHERE column = $2 AND key = $3")
    if err != nil {
        panic(err)
    }
    defer stmt.Close()

    // 使用翻译键和语言作为参数执行准备好的语句
    var translation string
    err = stmt.QueryRow(context.Background(), language, "en_US", "co").Scan(&translation)
    if err != nil {
        panic(err)
    }

    fmt.Println(translation)
}

更多关于Golang中如何判断查询语句是否安全的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中如何判断查询语句是否安全的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个存在SQL注入风险的实现。问题在于使用$1作为列名参数,这在PostgreSQL中是不安全的。列名、表名等标识符不能使用参数占位符。

不安全的原因

在PostgreSQL中,参数占位符只能用于,不能用于标识符(列名、表名等)。当尝试将列名作为参数传递时,它会被当作字符串值处理,而不是列名引用。

安全解决方案

方案1:使用白名单验证列名

import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v4/pgxpool"
)

// 定义允许的列名白名单
var allowedColumns = map[string]bool{
    "en_US": true,
    "fr_FR": true,
    "de_DE": true,
    "es_ES": true,
}

func getTranslation(dbpool *pgxpool.Pool, language, key string) (string, error) {
    // 验证列名是否在白名单中
    if !allowedColumns[language] {
        return "", fmt.Errorf("invalid language column: %s", language)
    }
    
    // 使用字符串拼接构造查询(因为列名已验证)
    query := fmt.Sprintf("SELECT %s FROM lang WHERE key = $1", language)
    
    var translation string
    err := dbpool.QueryRow(context.Background(), query, key).Scan(&translation)
    return translation, err
}

func main() {
    dbpool, err := pgxpool.Connect(context.Background(), "postgres://user:password@host:port/database")
    if err != nil {
        panic(err)
    }
    defer dbpool.Close()
    
    translation, err := getTranslation(dbpool, "en_US", "co")
    if err != nil {
        panic(err)
    }
    
    fmt.Println(translation)
}

方案2:使用CASE语句(更安全)

import (
    "context"
    "github.com/jackc/pgx/v4/pgxpool"
)

func getTranslationSafe(dbpool *pgxpool.Pool, language, key string) (string, error) {
    query := `
        SELECT CASE $1
            WHEN 'en_US' THEN en_US
            WHEN 'fr_FR' THEN fr_FR
            WHEN 'de_DE' THEN de_DE
            WHEN 'es_ES' THEN es_ES
            ELSE NULL
        END
        FROM lang 
        WHERE key = $2`
    
    var translation string
    err := dbpool.QueryRow(context.Background(), query, language, key).Scan(&translation)
    return translation, err
}

方案3:使用pgx的标识符引用(需要额外处理)

import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v4/pgxpool"
    "github.com/jackc/pgx/v4"
)

func getTranslationWithIdentifier(dbpool *pgxpool.Pool, language, key string) (string, error) {
    // 使用QuoteIdentifier安全地引用列名
    quotedColumn := pgx.Identifier{language}.Sanitize()
    
    query := fmt.Sprintf("SELECT %s FROM lang WHERE key = $1", quotedColumn)
    
    var translation string
    err := dbpool.QueryRow(context.Background(), query, key).Scan(&translation)
    return translation, err
}

判断查询语句是否安全的标准

  1. 所有用户输入必须使用参数化查询$1, $2等占位符)
  2. 标识符(列名、表名)不能作为参数传递
  3. 动态标识符必须通过白名单验证或安全引用函数处理
  4. 避免字符串拼接构造SQL语句,除非标识符已严格验证

在你的原始代码中,尝试将列名作为参数传递是无效的,会导致查询错误或返回字面字符串值,而不是预期的列数据。

回到顶部