You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何安全实现JSON字段的动态等值、模糊与不等查询?

问题描述

我有一个JSON类型的数据库列,内部包含多个字段,需要实现对该字段的等值、模糊(like)与不等查询,且列名是动态的,无法硬编码到WHERE子句中。

当前用GORM编写的查询代码生成了格式错误的MySQL语句,比如生成了log->>$'severity' = 'High',正确格式应该是log->>'$.severity'。如果用fmt.Sprintf拼接查询语句虽然能生成正确SQL,但会有SQL注入风险。相关代码如下:

func applyServerLogsQuery(db *gorm.DB, logType api.LogDestination_LogType, req *logs.GetLogsRequest) (*gorm.DB, error) {
    // set log type
    db = db.Where("log_type = ?", logType)

    // check for time
    if !req.StartTime.IsZero() {
        db = db.Where("time_stamp >= ?", req.StartTime)
    }

    if !req.EndTime.IsZero() {
        db = db.Where("time_stamp <= ?", req.EndTime)
    }

    // process filters
    for _, filter := range req.Filters {
        if len(filter.Values) == 0 {
            continue
        }

        property := filter.PropertyInfo.Name
        value := filter.Values[0]

        switch filter.FilterType {
        case logs.FilterTypeRegex:
            db = db.Where("log->>$? like ?", property, value)

        case logs.FilterTypeShould:
            db = db.Where("log->>$? = ?", property, value)

        case logs.FilterTypeExclude:
            db = db.Where("log->>$? != ?", property, value)
        }

        /*
            prefix := fmt.Sprintf("log->>'$.%s'", filter.PropertyInfo.Name)
            value := filter.Values[0]
            queryString := fmt.Sprintf("log->>'$.%s' like '%s'", filter.PropertyInfo.Name, value)

            log.Printf("Prefix %s value %s query %s", prefix, value, queryString)

            switch filter.FilterType {
            case logs.FilterTypeRegex:
                db = db.Where(queryString)
            }
        */
    }

    // handle order by time
    orderStr := "time_stamp"
    if req.OrderDesc {
        orderStr = fmt.Sprintf("%s desc", orderStr)
    } else {
        orderStr = fmt.Sprintf("%s asc", orderStr)
    }
    db = db.Order(orderStr)

    // handle limit
    db = db.Limit(int(req.Limit))

    return db, nil
}

错误生成的MySQL查询语句示例:
SELECT * FROM server_logs WHERE log_type = 2 AND time_stamp >= '2023-03-10 15:55:14.304' AND time_stamp <= '2023-03-10 15:55:24.441' AND log->>$'severity' = 'High' ORDER BY time_stamp desc

解决方法

方法1:用GORM表达式+参数绑定构建安全查询

利用GORM的gorm.Expr构建合法的JSON路径表达式,同时保留参数化查询避免注入风险:

for _, filter := range req.Filters {
    if len(filter.Values) == 0 {
        continue
    }

    property := filter.PropertyInfo.Name
    value := filter.Values[0]
    // 通过字符串拼接生成合法JSON路径,属性名作为参数传入
    jsonPathExpr := gorm.Expr("log->>'$.'||?", property)

    switch filter.FilterType {
    case logs.FilterTypeRegex:
        db = db.Where("? LIKE ?", jsonPathExpr, value)
    case logs.FilterTypeShould:
        db = db.Where("? = ?", jsonPathExpr, value)
    case logs.FilterTypeExclude:
        db = db.Where("? != ?", jsonPathExpr, value)
    }
}

这里用MySQL的||操作符拼接$.和动态属性名,所有变量都通过参数绑定传入,彻底规避SQL注入,同时生成log->>'$.severity'这类正确格式的SQL片段。

方法2:利用GORM v2内置JSON查询能力

如果使用GORM v2及以上版本,可直接拼接JSON路径模板,仅将查询值作为参数绑定:

for _, filter := range req.Filters {
    if len(filter.Values) == 0 {
        continue
    }

    property := filter.PropertyInfo.Name
    value := filter.Values[0]
    // 预构建合法的JSON路径模板
    jsonKey := fmt.Sprintf("log->>'$.%s'", property)

    switch filter.FilterType {
    case logs.FilterTypeRegex:
        db = db.Where(fmt.Sprintf("%s LIKE ?", jsonKey), value)
    case logs.FilterTypeShould:
        db = db.Where(fmt.Sprintf("%s = ?", jsonKey), value)
    case logs.FilterTypeExclude:
        db = db.Where(fmt.Sprintf("%s != ?", jsonKey), value)
    }
}

注意:若property来自用户输入,需先做合法性校验(比如仅允许字母、下划线等字符),避免恶意构造的路径引发风险。

方法3:直接修正SQL模板占位符写法

调整WHERE子句的模板格式,让GORM正确生成JSON路径:

for _, filter := range req.Filters {
    if len(filter.Values) == 0 {
        continue
    }

    property := filter.PropertyInfo.Name
    value := filter.Values[0]

    switch filter.FilterType {
    case logs.FilterTypeRegex:
        db = db.Where("log->>'$.'? LIKE ?", property, value)
    case logs.FilterTypeShould:
        db = db.Where("log->>'$.'? = ?", property, value)
    case logs.FilterTypeExclude:
        db = db.Where("log->>'$.'? != ?", property, value)
    }
}

这种写法直接在SQL模板中把$.和占位符组合,GORM会自动替换占位符为属性名,生成格式正确的SQL,同时查询值通过参数绑定保证安全。

内容的提问来源于stack exchange,提问作者Praveen Valtix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:22:51