使用Golang的Go-Redis驱动实现RedisSearch及防范注入攻击

使用Golang的Go-Redis驱动实现RedisSearch及防范注入攻击 我希望使用RedisSearch模块来支持如下代码所示的查询/搜索。

我使用的是Go-Redis驱动,据我所知,这个驱动(或者其他驱动?)没有参数化的方式来调用RedisSearch,因此你必须使用rdb.Do()命令。

这可能会暴露注入攻击的漏洞,因为这些值将由用户通过网站输入。

或许,我可以清理这些值以降低风险,但这也会给流程增加一个步骤和一个潜在的故障点。

有谁知道是否有其他方法可以以更参数化的方式实现这一点?

package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"log"
)

var ctx = context.Background()

func main() {
	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", 
		DB:       0,  
	})

	query := "FT.SEARCH housesIdx \"@suburb:{sydney} @price:[1000000 1200000] @bedrooms:[4 4] @garages:[1 1] @bathrooms:[2 2]\""

	res, err := rdb.Do(ctx, query).Result()
	if err != nil {
		log.Fatalf("Error executing search query: %v", err)
	}

	fmt.Printf("Search results: %+v\n", res)
}

更多关于使用Golang的Go-Redis驱动实现RedisSearch及防范注入攻击的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢!我已经采纳了您的建议,将使用Postgres,并配合Go的Map和其他Go结构体/切片来实现缓存。

更多关于使用Golang的Go-Redis驱动实现RedisSearch及防范注入攻击的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


直接回答你的问题:你可以构建自己的检查。使用正则表达式检查用户输入中是否有异常内容,然后构建查询字符串。

  1. 你为什么使用Redis?在我看来,Go的一大优点是你可以访问goroutine之间的共享内存。不需要Redis。少了一个可能出问题或制造麻烦的东西需要操心。

  2. 如果你需要在内存中使用SQL,你也可以直接使用你喜欢的数据库(如Postgres或Maria)中的内存表。

此致

在Go-Redis中使用RedisSearch时,确实需要注意防范注入攻击。虽然Go-Redis没有专门的RedisSearch参数化方法,但可以通过以下方式安全构建查询:

package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"log"
	"strings"
)

var ctx = context.Background()

// 安全构建RedisSearch查询
func buildSafeSearchQuery(index string, filters map[string]string, numericRanges map[string][2]float64) string {
	var queryParts []string
	
	// 处理文本过滤条件
	for field, value := range filters {
		// 清理输入值,移除特殊字符
		cleanValue := sanitizeRedisSearchValue(value)
		queryParts = append(queryParts, fmt.Sprintf("@%s:{%s}", field, cleanValue))
	}
	
	// 处理数值范围条件
	for field, rangeVals := range numericRanges {
		queryParts = append(queryParts, fmt.Sprintf("@%s:[%v %v]", field, rangeVals[0], rangeVals[1]))
	}
	
	// 构建完整查询
	queryString := strings.Join(queryParts, " ")
	return fmt.Sprintf("FT.SEARCH %s \"%s\"", index, queryString)
}

// 清理RedisSearch值,防范注入攻击
func sanitizeRedisSearchValue(input string) string {
	// 移除RedisSearch特殊字符
	disallowed := []string{"\"", "'", "\\", "@", ":", "{", "}", "[", "]", "(", ")", "|", "-", "+", "*", "~", "$"}
	result := input
	for _, char := range disallowed {
		result = strings.ReplaceAll(result, char, "")
	}
	return result
}

// 使用参数化方式执行查询
func executeSafeSearch(rdb *redis.Client, index string, filters map[string]string, numericRanges map[string][2]float64) (interface{}, error) {
	query := buildSafeSearchQuery(index, filters, numericRanges)
	return rdb.Do(ctx, query).Result()
}

func main() {
	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})

	// 示例:安全构建查询参数
	filters := map[string]string{
		"suburb": "sydney",
	}
	
	numericRanges := map[string][2]float64{
		"price":     {1000000, 1200000},
		"bedrooms":  {4, 4},
		"garages":   {1, 1},
		"bathrooms": {2, 2},
	}

	// 执行安全查询
	res, err := executeSafeSearch(rdb, "housesIdx", filters, numericRanges)
	if err != nil {
		log.Fatalf("Error executing search query: %v", err)
	}

	fmt.Printf("Search results: %+v\n", res)
}

对于更复杂的查询,可以使用结构化的查询构建器:

type RedisSearchQuery struct {
	Index    string
	Filters  []Filter
	Ranges   []RangeFilter
	Options  map[string]interface{}
}

type Filter struct {
	Field string
	Value string
}

type RangeFilter struct {
	Field string
	Min   interface{}
	Max   interface{}
}

func (q *RedisSearchQuery) Build() string {
	var parts []string
	
	for _, filter := range q.Filters {
		cleanValue := sanitizeRedisSearchValue(filter.Value)
		parts = append(parts, fmt.Sprintf("@%s:{%s}", filter.Field, cleanValue))
	}
	
	for _, rng := range q.Ranges {
		parts = append(parts, fmt.Sprintf("@%s:[%v %v]", rng.Field, rng.Min, rng.Max))
	}
	
	queryStr := strings.Join(parts, " ")
	return fmt.Sprintf("FT.SEARCH %s \"%s\"", q.Index, queryStr)
}

// 使用示例
func exampleUsage() {
	query := &RedisSearchQuery{
		Index: "housesIdx",
		Filters: []Filter{
			{Field: "suburb", Value: "sydney"},
		},
		Ranges: []RangeFilter{
			{Field: "price", Min: 1000000, Max: 1200000},
			{Field: "bedrooms", Min: 4, Max: 4},
		},
	}
	
	safeQuery := query.Build()
	fmt.Println(safeQuery)
}

这些方法通过输入验证和结构化查询构建,可以有效防范RedisSearch注入攻击。

回到顶部