Redis Stack中FT.SEARCH查询注入防范及最佳实践确认
Redis Stack全文搜索的查询注入风险与最佳实践
问题分析
当用redis-py客户端在Redis Stack上,仅针对text字段执行全文搜索时,直接通过字符串插值拼接用户提供的query_str会存在查询注入风险。比如下面的search_1实现:
from redis import Redis from redis.commands.search.query import Query client = Redis.from_url("redis://localhost:6379") def search_1(query_str: str): query = Query(f"@text:{query_str}") return client.ft("idx:test").search(query)
如果用户输入query_str = "query_for_text @other_field:query_for_other_field",实际执行的FT.SEARCH命令会跨字段搜索,完全违背了仅搜索text字段的需求。
参数化查询的有效性确认
你推测的search_2方案是完全正确的:
def search_2(query_str: str): params = {"query_str": query_str} query = Query("@text:$query_str").dialect(2) return client.ft("idx:test").search(query, params)
- Redis Stack的
FT.SEARCH参数化查询(需指定dialect 2及以上)会将参数值作为完整短语处理,不会解析其中的RedisSearch语法符号(比如@、:),从根源上避免了注入。 - 按照Redis的设计规则,参数只能用于替换查询中的值部分,无法用来替换字段名这类语法结构,刚好匹配我们仅限定
text字段搜索的场景。
处理未净化用户输入的最佳实践
这里有两种可靠方案,优先推荐参数化查询:
- 优先使用
PARAMS参数化查询:这是最安全、最省心的方式,无需手动处理输入,由Redis Stack自动保障查询的安全性,完全避免注入风险。 - 手动转义特殊字符:如果受限于Redis版本无法使用参数化,需要手动转义RedisSearch的特殊语法字符(包括
@、:、"、(、)、*、?、~、-、+等),并将转义后的内容作为短语搜索。示例转义函数:
def escape_redis_search_query(query_str: str) -> str: special_chars = r'@:"()*?~-+' escaped = ''.join(f'\\{c}' if c in special_chars else c for c in query_str) return f'"{escaped}"'
使用时将查询拼接为Query(f"@text:{escape_redis_search_query(query_str)}"),双引号能确保转义后的内容被当作完整短语处理,不会触发语法解析。
注意:这种注入风险和Redis协议层面的注入不是一回事,协议层面的注入针对的是客户端向服务器发送的命令结构,而这里的风险是RedisSearch查询语法层面的注入。
内容的提问来源于stack exchange,提问作者Tom Lin
相关产品推荐
相关产品推荐

